Agent Wranglers

APS-12Required

Verify every token

Check the signature, algorithm, expiry and audience of every signed token, such as a JWT. Give access tokens a life of 15 minutes or less. This is the one home of that limit.

How to do it

Use a maintained library and pin the algorithms you accept. Check exp, iss and aud on every token. An API accepts only access tokens, never an ID token from a login. When your issuer follows RFC 9068, check that the typ header is at+jwt. Many providers still issue access tokens with typ: JWT. There, tell the two apart by audience, since an ID token's aud is the client, not your API. Never let the token's own header pick the algorithm or fetch a key, such as alg, jku or x5u. Use kid only to pick from your own list of keys. An HMAC key needs at least 256 random bits. Renew access tokens with a refresh token that the server can revoke and rotates, as rotate refresh tokens and catch their reuse says. A token that is still valid must stop working after sign-out or a role change within the limit in end sessions when access changes. So check a revocation list or a "valid after" time on each request, or keep the token life shorter than that limit.

Examples

JavaScript example

// Node with jose: algorithm, token type, issuer and audience are fixed by the server
import { jwtVerify } from 'jose';
const { payload } = await jwtVerify(token, publicKey, {
  algorithms: ['ES256'], typ: 'at+jwt', issuer: 'https://login.example.com', audience: 'https://api.example.com',
});

Python example

# Python with PyJWT
claims = jwt.decode(token, public_key, algorithms=["ES256"],
                    issuer="https://login.example.com", audience="https://api.example.com")
if jwt.get_unverified_header(token).get("typ") not in ("at+jwt", "application/at+jwt"):
    raise jwt.InvalidTokenError("not an access token")

How to check

Send a token with its signature removed and another with alg set to none. Send an expired one, one for another audience, an ID token from the same login and one signed with a guessed key such as secret. Every request returns 401. A freshly issued access token has exp at most 900 seconds after iat.

Sources

For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/application-security/authentication-and-sessions/token-checks">Agent Wranglers application security playbook</a>. In code you write, add a comment next to it with the same link.