Skip to main content Skip to sidebar

JWT vs Opaque Access Tokens

When you issue access tokens for an API, you face an early architectural decision: should the token carry its own meaning, or should it just be a reference? A JWT (JSON Web Token) is self-contained, it packs claims and a signature into the string itself. An opaque token is a random, meaningless identifier that points to state stored on the server. Both are valid OAuth 2.0 access token formats, and the choice shapes how validation, revocation, and scaling work for the rest of the system. This article compares the two approaches and the trade-offs that should drive the decision.

Core Concepts

JWT Access Tokens

A JWT is a signed, base64url-encoded structure with three parts separated by dots: a header, a payload, and a signature. The payload holds claims, the user identifier, scopes, expiry, issuer, and any other data the API needs. Because the signature is verifiable with a public key (or shared secret), any service can validate the token and read its claims without contacting the issuer.

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9 . eyJzdWIiOiIxMjM0NTY3ODkw...iaWF0IjoxNzE5MTQ2NDAwfQ . NHVkQ8nQ7m...4fwpMeJf36
\__________________________________/   \________________________________________________/   \_____________________/
               header                                       payload                                signature

Decoded, the payload looks like this:

{
  "sub": "1234567890",
  "scope": "read write",
  "iss": "https://auth.example.com",
  "exp": 1719150000,
  "iat": 1719146400
}

Key characteristics:

  • Self-contained: All the information needed to authorize a request lives in the token
  • Stateless validation: A resource server verifies the signature and checks exp locally, no network call to the issuer
  • Cryptographically signed: Tampering with claims invalidates the signature
  • Readable: The payload is encoded, not encrypted, anyone holding the token can read the claims

Opaque Access Tokens

An opaque token is a high-entropy random string with no internal structure. It means nothing on its own.

2YotnFZFEjr1zCsicMWpAA

The authorization server stores the associated state, who the token belongs to, its scopes, and its expiry, in a database or cache. To validate the token, a resource server must ask the authorization server what it represents, typically via the OAuth 2.0 Token Introspection endpoint (RFC 7662).

Key characteristics:

  • Reference only: The token is a lookup key, all meaning lives server-side
  • Stateful validation: Each validation requires a lookup against the token store
  • Opaque by design: Clients and resource servers learn nothing from the string itself
  • Trivially revocable: Deleting the server-side record invalidates the token instantly

Validation Flow

The fundamental difference is where validation happens.

A JWT is validated locally by the resource server. There is no round trip to the authorization server on the hot path.

+--------+   request + JWT    +-------------------+
| Client | -----------------> |  Resource Server  |
+--------+                    |-------------------|
                              | verify signature  |
                              | check exp / scope |
                              | (local only)      |
                              +-------------------+
                                        ^
                                        | cached public key
                                        | (not per request)
                                        |
                              +-------------------+
                              |    Auth Server    |
                              |  (JWKS endpoint)  |
                              +-------------------+

An opaque token requires the resource server to call the authorization server for every request (or to cache the result for a short window).

+--------+  request + token   +-------------------+
| Client | -----------------> |  Resource Server  |
+--------+                    +-------------------+
                                        |
                                        | introspect
                                        | (per request)
                                        v
                              +-------------------+
                              |    Auth Server    |
                              |-------------------|
                              | token store       |
                              | lookup + state    |
                              +-------------------+

This single distinction, local verification versus remote lookup, drives most of the other trade-offs.

Revocation

Revocation is where the two approaches diverge most sharply.

An opaque token can be revoked immediately. Because the authorization server holds the state, deleting or flagging the record means the very next introspection call fails. This matters for logout, compromised credentials, or an administrator disabling an account.

A JWT cannot be revoked on its own. Once issued, it is valid until exp regardless of what happens server-side, because resource servers never check back. Teams work around this with several patterns:

  • Short expiry: Issue access tokens with a lifetime of 5 to 15 minutes, paired with a longer-lived refresh token. Damage from a leaked token is bounded by its short window
  • Denylist: Maintain a list of revoked token identifiers (jti) that resource servers check. This reintroduces shared state and partially defeats the stateless benefit
  • Rotating signing keys: Invalidates all tokens signed with a key at once, useful for a broad emergency revocation but too blunt for a single user

The short-expiry plus refresh-token pattern is the most common compromise: it keeps validation stateless while capping the exposure window.

Token Size and Transport

An opaque token is small, often 20 to 40 bytes. A JWT carries its claims and signature, so it is typically several hundred bytes to over a kilobyte, especially with RSA signatures or many claims.

This adds up. The token rides in the Authorization header on every request, so a large JWT increases bandwidth and can bump against header size limits in proxies and load balancers (commonly 8 KB total). Keep JWT payloads lean, store only what the resource server needs to authorize the request, not a full user profile.

Security Considerations

Both formats are secure when implemented correctly, but they fail in different ways.

JWT pitfalls:

  • The alg: none attack: A token claiming no signature algorithm must be rejected. Always pin the expected algorithm during verification
  • Algorithm confusion: An attacker tricks a server into verifying an RS256 token as HS256, using the public key as the HMAC secret. Validate alg against an allowlist
  • Claims are not encrypted: Never put secrets in a JWT payload, it is readable by anyone holding the token
  • Leaked tokens stay valid: Until expiry, a stolen JWT works, hence short lifetimes

Opaque token considerations:

  • Entropy matters: The token must be generated from a cryptographically secure random source with enough length to resist guessing
  • Store availability: The token store becomes a hard dependency on the request path, its outage breaks authorization everywhere
  • Introspection trust: The introspection channel itself must be authenticated so attackers cannot probe token validity

Hybrid Approaches

The choice is not strictly binary. Common middle grounds include:

  • Introspection caching: Cache opaque-token introspection results for a few seconds. This trades a small revocation delay for far fewer lookups, blending stateless speed with stateful control
  • Phantom tokens: The client holds an opaque token, but an API gateway exchanges it for a short-lived JWT that internal services validate locally. External revocation stays instant while internal traffic stays stateless
  • Reference tokens with short JWTs: Long-lived opaque refresh tokens with short-lived JWT access tokens, the OAuth 2.0 default for many providers

Choosing Between Them

Use JWTs when:

  • You have many resource servers or microservices that should validate independently without a shared dependency
  • Low per-request latency matters and you want to avoid a network hop on every call
  • Token traffic crosses trust boundaries where a public-key check is convenient
  • You can accept bounded revocation latency by using short token lifetimes

Use opaque tokens when:

  • Immediate revocation is a hard requirement, for example regulated environments or high-value sessions
  • Tokens are issued and consumed within a single trust domain where introspection is cheap
  • You want to keep token contents fully private from clients and intermediaries
  • The authorization server can comfortably handle introspection load

Comparison Summary

AspectJWTOpaque
ValidationLocal, statelessRemote introspection
RevocationHard, needs workaroundsImmediate
SizeLarge (hundreds of bytes+)Small (20-40 bytes)
Per-request latencyLow (no network hop)Higher (lookup, unless cached)
ContentsReadable by holderMeaningless to holder
ScalingScales with resource serversBounded by token store
Failure dependencySigning key availabilityToken store availability

Conclusion

JWTs trade revocation control for stateless, low-latency validation that scales naturally across many services. Opaque tokens trade a per-request lookup for instant revocation and full secrecy of token contents. Most large systems end up blending the two: short-lived JWT access tokens for speed, opaque refresh tokens for control, and sometimes a gateway that exchanges one for the other. Start from your revocation and latency requirements, those constraints almost always point clearly to one side.