WorkOS published a guide to verifying access tokens in your own API that spends most of its length on the part every other token-verification tutorial skips: what to return when verification fails for a reason that is not the caller’s fault. The line worth pinning to a wall is theirs — ERR_JWKS_TIMEOUT is a 503, not a 401, “because the problem is yours, not the caller’s. Returning 401 there tells every signed-in user their session is invalid over a transient network blip.” That is a one-character difference in a status code that turns a fifteen-second JWKS outage into a mass logout event.
The mechanics around it are unremarkable and correct: pull cached public keys from https://api.workos.com/sso/jwks/{clientId}, verify the signature, then require audience, issuer and expiration claims rather than trusting a well-formed token. Two pieces of advice in the post are more opinionated than they look. The first is to key user records on sub, the WorkOS user ID, and never on email — email is a mutable attribute wearing an identifier’s clothes. The second is to authorize on permissions rather than role, because “roles get renamed and re-scoped by customer admins, and code that branches on the string ‘admin’ breaks silently when someone creates ‘Admin’.” Silently is the operative word; a role-string comparison has no failure mode that shows up in a log.
Every claim in that post maps to a discrete surface in the catalog, which is what a well-decomposed API estate looks like. WorkOS carries 41 API pages, and the article’s flow walks four of them in order: the JWT Template API is where the audience claim the post tells you to require actually gets configured; the Session Tokens API issues and refreshes what your middleware validates; the Permissions API is the surface behind “authorize on permissions, not on role”; and the SSO API is where the JWKS endpoint lives. A post about the shape of a token is really a post about four APIs agreeing on one claim set.
WorkOS scores 49.4, developing on the Kin Score, with contract quality at 69.8 and developer ergonomics at 63.1 — high marks earned by publishing their OpenAPI in the open rather than only rendering it. Their Agent Readiness is 38.3, agent-ready, on verified idempotency, verified OpenAPI examples, documented rate limits, and a documented MCP server. Which makes the one gap conspicuous: error_semantics is unlit. The company that just wrote the clearest available explanation of why 503 and 401 are different answers has not yet put that distinction in a machine-readable place. Their blog knows it. Their contract does not say it. That is a cheap fix and it is worth more to an agent than anything else on the page — the whole argument of the post is that a client has to tell “retry” from “re-authenticate,” and a client that reads specs cannot.