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
- RFC 8725 JSON Web Token Best Current Practices
- RFC 7519 JSON Web Token (JWT)
- RFC 9068 JWT Profile for OAuth 2.0 Access Tokens
Related rules
- API-38 Authenticate with tokens in headers in API design
- ACC-28 Let people sign out of one device or all of them in Accounts and identity
- ACC-29 End sessions when access changes in Accounts and identity
- ACC-38 Apply removed roles within the revocation limit in Accounts and identity
- ACC-63 Listen for security signals from the customer's provider in Accounts and identity
- ACC-68 End the session when the person signs out at the provider in Accounts and identity
- ACC-80 Rotate refresh tokens and catch their reuse in Accounts and identity
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.