Authentication vs authorization: JWTs, OAuth2, and OIDC
Separate who you are from what you may do, and read a token well enough to know what it actually proves.
Two questions that get conflated constantly. Authentication asks who are you. Authorization asks what may you do. A system can be certain of your identity and still correctly refuse you.
A JSON Web Token is a signed, base64-encoded set of claims. Signed, not encrypted: anyone holding it can read the payload. That single fact is the source of a lot of accidental disclosure.
{
"iss": "https://issuer.example.com", // who minted this
"sub": "agent_01H...", // who it is about
"aud": "https://api.example.com", // who it is FOR
"exp": 1767225600, // when it dies
"iat": 1767224700,
"scope": "secrets:read" // what it permits
}Always validate iss, aud, and exp. A token that is cryptographically valid but was minted for a different audience is a confused-deputy attack waiting to happen, and it will pass a naive signature check.
- OAuth2 is an authorization framework: it issues access tokens that say what a bearer may do.
- OIDC is a thin identity layer on top of OAuth2; it adds an id_token that says who the user is.
- A bearer token means exactly what it sounds like: whoever holds it can use it. There is no binding to the holder unless you add one.
Because bearer tokens are transferable, the main defence is time. A token that lives fifteen minutes is a far smaller prize than one that lives forever, and it makes the exchange pattern worth the complexity: hold a long-lived credential somewhere safe, exchange it for a short-lived token at the point of use.
JWKS, a published set of public keys, lets any party verify a token without holding a shared secret. Each key has a kid so the verifier knows which one to use, and keys can rotate without coordinating with every consumer.
Check your understanding
3 questionsIs the payload of a signed JWT confidential?
Why must a verifier check the aud claim?
What is the primary defence against a stolen bearer token?