A JWT Is Not Authorization
Header, claims, signature, and the small details that matter when agents start carrying tokens.
A detour from The Agent Authorization Standard We Already Had, for anyone who nodded along at "JWT" and hoped nobody would ask a follow-up question.
JSON Web Tokens show up in almost every conversation about agent identity and authorization. Then, almost immediately, people start using "JWT," "access token," "identity," and "authorization" as if they are the same thing. They are not. A signed blob appears, everybody relaxes, and authorization is taken for granted as everyone quietly pretends they know how it works.
This is a short refresher for the rest of us who want to stop pretending. I am not going to turn this into a cryptography lecture. I just want to make sure we agree on what is inside a JWT, what the signature proves, and what the resource server still has to decide for itself.
We are often quite fast and loose with the term "body" when referring to what's inside a JWT. The more precise term is the JWT payload, or JWT Claims Set. It's the JSON object containing the claims conveyed by the token. That's it.1
Also remember that an OAuth access token is not always a JWT. This piece applies when the authorization server issues a JWT, specifically the signed compact form commonly carried as a bearer token.
The Three Parts
A signed JWT using the JSON Web Signature (JWS) Compact Serialization normally appears as three base64url-encoded values separated by periods:2
BASE64URL(header).BASE64URL(payload).BASE64URL(signature)
Base64url encoding makes the value safe to transport in URLs and HTTP headers. It does not encrypt the content. Anybody holding a normal signed JWT can decode its header and payload. The signature protects integrity and helps the recipient verify who issued it.
Base64 has spent decades being mistaken for encryption and seems unlikely to correct anyone now.
1. Header
The header describes how the token was secured:
{
"alg": "RS256",
"typ": "JWT",
"kid": "auth-key-2026-09"
}
algidentifies the signing algorithm. The recipient must verify that it is one the application accepts.typindicates that the object is a JWT. It is commonly present but optional.kidis a key identifier that helps the recipient select the correct public key for signature validation.
2. Payload, or JWT Claims Set
This is the part we spend most of the article discussing. Here is a decoded example of the token the coding agent presents to the deployment MCP:
{
"iss": "https://auth.example.com",
"sub": "nadia",
"aud": "deployment-mcp",
"iat": 1760000000,
"exp": 1760000900,
"jti": "at-123",
"scope": "deploy.production",
"act": {
"sub": "coding-agent",
"act": {
"sub": "personal-assistant"
}
}
}
issidentifies the authorization server that issued the token.subidentifies Nadia, whose authority is being exercised.audidentifies the intended recipient. The deployment MCP must reject a token not intended for it.iatrecords when the token was issued.exprecords when it expires. These time claims use NumericDate values, measured in seconds since the Unix epoch.jtiis a unique token identifier that can support logging or replay controls.scopelimits the capability todeploy.production.- The outermost
actidentifies the coding agent as the current actor. The nestedactpreserves the personal assistant as the previous actor.
You might see may_act in an earlier subject token to state that a particular agent is eligible to become the actor. It is not included here because the deployment MCP is the final resource in this example and is not being authorized to continue the delegation chain.
3. Signature
The issuer signs the base64url-encoded header and payload. In simplified form:
SIGN(
BASE64URL(header) + "." + BASE64URL(payload),
issuer-private-key
)
The deployment MCP validates that signature using a trusted key associated with the issuer. A valid signature shows that the protected header and payload have not been tampered with since the issuer signed them. It does not mean the request is automatically authorized.
A signed mistake is still a mistake. It simply has better provenance.
The deployment MCP must still validate the trusted issuer, intended audience, expiration, scope, current actor, and its own local policy. A token can be correctly signed and still be completely wrong for the resource receiving it. If you buy a ticket from Lufthansa, you can't just get on any plane at the airport that has Lufthansa written on it.
A JWT is a container for claims. The authorization decision comes from validated claims plus policy, not from the existence of the token itself.
The Part People Forget
A valid signature does not tell you whether the token belongs at your service, whether it has expired, whether its scope permits the action, or whether the current actor is allowed to exercise the subject's authority. It only tells you that the signed content has not changed and that it was signed by a key you associate with the issuer.
The payload is the important part of the token once its signature is verified! There we can get all of the information we need about what this token actually allows the client using it to do. That is how we can get spicy with the claims in the token!
That distinction matters even more in agent systems. A token can be perfectly valid and still represent the wrong user, the wrong agent, the wrong audience, or far too much authority.