Skip to content

JWT claims and authorization boundaries

How to decide which claims can be trusted, which must be rechecked, and where JWTs should stop carrying authority.

Published on 3 min read

JWT claims are convenient because they move facts close to the request. That convenience turns dangerous when claims start replacing authorization. A signed token can prove that an issuer made a statement at a point in time. It does not prove that the statement is still true, that the resource belongs to the same tenant, or that a user should perform a specific action right now.

Identity is not authorization

sub identifies a principal. scope or roles describe broad permission buckets. tenant_id may describe the tenant selected at login. None of those claims automatically answers, "Can this user approve this invoice?" The resource still needs ownership checks, tenant boundary checks, and sometimes state checks. Attackers look for places where a claim is treated as a database join that never happens.

A common bug appears in multi-tenant APIs: the handler trusts tenant_id from the token and uses it to filter records, but the route also accepts a tenant or account identifier in the path. If those values disagree, the system must reject the request. Silently preferring one value creates confusing behavior. Trusting the path creates an authorization bypass.

Claims that age badly

Roles, plan levels, organization membership, and feature entitlements can change while a token is still valid. If access tokens live for an hour, removing an admin role may take an hour to matter unless the API checks current state. For low risk read paths, that may be acceptable. For billing, exports, admin actions, and destructive operations, stale claims are usually not acceptable.

A practical boundary

Use JWTs for authentication facts and coarse routing decisions: issuer, subject, audience, expiration, session id, and maybe a compact scope. Use server side checks for resource authorization. When performance matters, cache authorization decisions with short TTLs and explicit invalidation rather than stuffing every permission into the token.

Testing claim abuse

During a pentest, mutate one claim at a time and watch for changes in object access. Change tenant_id, org_id, scope, role, email_verified, and feature flags. Keep the signature valid if you control a test issuer, or use low privilege tokens across endpoints to see where claims are decoded before verification. Check GraphQL especially, because resolvers sometimes apply authorization inconsistently.

Design rule

A JWT should carry enough information to identify the caller and choose the right verifier, not enough information to bypass the authorization model. When a claim affects money, data boundaries, or admin power, the API should be able to explain why it trusted that claim at that moment.

Related articles

How exp, nbf, iat, skew windows, retries, and regional clocks affect real JWT validation decisions.
How to inspect JWT problems safely in logs, support tickets, CLI tools, screenshots, and incident channels without spreading bearer tokens.
Operational guidance for JWT signing keys: generation, storage, KMS use, rotation, access control, audit trails, and emergency response.