---
title: "Verify every token · Agent Wranglers"
url: https://agentwranglers.se/playbook/application-security/authentication-and-sessions/token-checks
id: APS-12
level: Required
applies: accounts
risk: takeover
updated: 2026-10-27
build: a2a6bdfed830
tokens: 1386
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Verify every token

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [Authentication and sessions](https://agentwranglers.se/playbook/application-security/authentication-and-sessions.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers.md). Updated 2026-10-27.

- ID: APS-12
- Level: Required
- Chapter: [Authentication and sessions](https://agentwranglers.se/playbook/application-security/authentication-and-sessions.md)
- Rule: 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.
- 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`.
- Risk: An account or system taken over
- Applies when: The project has user accounts.
- How: 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](https://agentwranglers.se/playbook/accounts/sessions-and-security-activity/refresh-token-rotation.md) 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](https://agentwranglers.se/playbook/accounts/sessions-and-security-activity/end-sessions-on-change.md). So check a revocation list or a "valid after" time on each request, or keep the token life shorter than that limit.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### JavaScript example

```js
// 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
# 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")
```

## Sources

- [RFC 8725 JSON Web Token Best Current Practices](https://www.rfc-editor.org/rfc/rfc8725)
- [RFC 7519 JSON Web Token (JWT)](https://www.rfc-editor.org/rfc/rfc7519)
- [RFC 9068 JWT Profile for OAuth 2.0 Access Tokens](https://www.rfc-editor.org/rfc/rfc9068)

## Related rules

- API-38 [Authenticate with tokens in headers](https://agentwranglers.se/playbook/api-design/api-security/token-auth.md) in API design
- ACC-28 [Let people sign out of one device or all of them](https://agentwranglers.se/playbook/accounts/sessions-and-security-activity/revoke-sessions.md) in Accounts and identity
- ACC-29 [End sessions when access changes](https://agentwranglers.se/playbook/accounts/sessions-and-security-activity/end-sessions-on-change.md) in Accounts and identity
- ACC-38 [Apply removed roles within the revocation limit](https://agentwranglers.se/playbook/accounts/roles-and-permissions/role-changes-immediate.md) in Accounts and identity
- ACC-63 [Listen for security signals from the customer's provider](https://agentwranglers.se/playbook/accounts/business-sign-on-and-provisioning/shared-signals.md) in Accounts and identity
- ACC-68 [End the session when the person signs out at the provider](https://agentwranglers.se/playbook/accounts/business-sign-on-and-provisioning/back-channel-logout.md) in Accounts and identity
- ACC-80 [Rotate refresh tokens and catch their reuse](https://agentwranglers.se/playbook/accounts/sessions-and-security-activity/refresh-token-rotation.md) in Accounts and identity

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/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.

- Previous: [Store passwords with a slow hash](https://agentwranglers.se/playbook/application-security/authentication-and-sessions/password-storage.md)
- Next: [Compare secrets in constant time](https://agentwranglers.se/playbook/application-security/authentication-and-sessions/constant-time-compare.md)
- [Authentication and sessions](https://agentwranglers.se/playbook/application-security/authentication-and-sessions.md)
