Skip to content
Ashish.
All posts
Diagram illustrating the distinction between the OAuth 2.0 protocol flow and the JWT data format.

OAuth 2.0 vs JWT: Understanding the Relationship

An examination of the relationship between OAuth 2.0 and JSON Web Tokens, covering opaque tokens, token format selection, and JWT best practices.

By Ashish SrivastavaPart 6 of OAuth 2.0 Series

The most common misconception in API security is treating OAuth 2.0 and JSON Web Tokens (JWT) as competing technologies. They are not. OAuth 2.0 is a protocol that defines how a user grants a third-party application access to their resources without sharing their credentials. It specifies the roles (Resource Owner, Client, Authorization Server, Resource Server) and the flows (Authorization Code, Client Credentials). JWT, defined in RFC 7519, is simply a standard for representing claims securely between two parties. It is a format, like JSON or XML, but with specific cryptographic rules. In the architecture of a modern API, OAuth 2.0 dictates the conversation, and the token format (whether JWT or opaque) is just the content of that conversation.

The Mechanism of Decoupling

Consider a scenario involving Alice (the user), Bob's App (the Client), and a Photo Service (the Resource Server). When Alice logs in via Bob's App, the Authorization Server generates a token. If the Authorization Server issues an opaque token, it looks like a random string of characters, e.g., xJ9kL2mN4pQ8rT. To the Resource Server, this string means nothing. The Resource Server cannot validate it locally. Instead, it must make a synchronous network call back to the Authorization Server's introspection endpoint to ask, "Is xJ9kL2mN4pQ8rT valid, and does it have permission to access Alice's photos?" This mechanism ensures that if Alice revokes access immediately, the next request to the Resource Server will fail because the introspection check returns invalid. This is the mechanism of centralized state management.

GET /introspect HTTP/1.1
Host: auth.example.com
Authorization: Basic c3ViZWN0OjIzNDU2Nzg5
 
token=xJ9kL2mN4pQ8rT
token_type_hint=access_token

If the Authorization Server instead issues a JWT, the token looks like three base64url-encoded strings separated by dots: header.payload.signature. The Resource Server no longer needs to call the Authorization Server for every request. It validates the signature locally using a public key (in the case of asymmetric signing) or a shared secret (symmetric). The mechanism here shifts the trust boundary from the Authorization Server to the Resource Server. The Resource Server trusts the signature to prove the token was issued by a trusted party. This removes the network round-trip, improving latency, but it introduces a different problem: revocation.

The Token Format Selection Strategy

This brings us to the token format selection strategy. The choice between opaque tokens and JWTs is not about which is "better," but which fits the operational constraints. If your system requires immediate, granular revocation of access without waiting for a token to expire (e.g., an employee leaves the company and their access must be cut instantly across all devices), opaque tokens are the superior mechanism. With opaque tokens, revocation is a database update at the Authorization Server. With JWTs, revocation requires maintaining a "blocklist" of token IDs (jti) at every Resource Server or relying on a very short expiration time (TTL), which forces clients to refresh tokens frequently.

However, JWTs offer a distinct advantage in distributed systems where the Resource Servers are stateless and scale horizontally. If you have 500 microservices validating tokens, requiring each to query a central introspection endpoint creates a single point of failure and significant latency. By embedding the user's permissions directly into the JWT payload, the Resource Server can validate and authorize the request entirely offline, provided the signature is valid. In this architecture, the Resource Server acts as the client initiating the request to the central Authorization Server's introspection endpoint when using opaque tokens; this dependency on the central service for every validation creates the bottleneck and the single point of failure, whereas JWTs eliminate this specific dependency.

A technical architecture diagram comparing two authentication flows side-by-side. Left side: OAuth 2.0 with Opaque Token showing a Resource Server making a synchronous network call to an Authorization Server for introspection. Right side: OAuth 2.0 with JWT showing a Resource …
{
  "header": {
    "alg": "RS256",
    "typ": "JWT"
  },
  "payload": {
    "iss": "https://auth.example.com",
    "sub": "alice_123",
    "aud": "photo-api",
    "roles": ["read", "write"],
    "exp": 1715628000
  },
  "signature": "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c..."
}

JWT Implementation Best Practices

When implementing JWTs, the mechanism of claim validation is where most security failures occur. A naive implementation might only check the signature. This is insufficient. An attacker could generate a new JWT with a valid signature (if they stole the secret) or, worse, exploit token confusion attacks if the algorithm is not strictly enforced. For instance, if the server accepts none as an algorithm, an attacker can strip the signature entirely. The mechanism requires the Resource Server to strictly validate the alg header in the JWT, ensuring it matches the expected key type (e.g., rejecting HS256 if only RS256 keys are configured).

Furthermore, the aud (audience) and iss (issuer) claims are critical for preventing token confusion. If a token is intended for photo-api, but an attacker steals a token meant for admin-api, the photo-api should reject it even if the signature is valid. The mechanism here is a strict string comparison: the aud claim must match the unique identifier of the service receiving the token. Without this check, a token leaked from one service can be replayed in another, granting unintended privileges.

Opinion: While JWTs are often praised for their stateless nature, storing them in browser localStorage is a poor practice for web applications. This exposes the token to Cross-Site Scripting (XSS) attacks, where malicious scripts can read the storage and exfiltrate the token. The secure mechanism is to store the token in an HttpOnly, Secure cookie. This prevents JavaScript from accessing the token, mitigating XSS theft. However, this introduces Cross-Site Request Forgery (CSRF) risks, which must be mitigated using the SameSite attribute and CSRF tokens. The tradeoff is clear: localStorage is easier to implement but less secure against XSS; HttpOnly cookies are more complex to manage but safer against script-based theft.

Finally, the relationship between OAuth 2.0 and JWT extends to the scope of the token. In OAuth 2.0, the client requests specific scopes (e.g., read:photos, write:profile). The Authorization Server encodes these scopes into the token. If the token is a JWT, the scope claim becomes part of the payload. The Resource Server reads this claim to determine what actions the client is permitted to perform. If the token is opaque, the Resource Server asks the Authorization Server for the scopes associated with the token ID. The mechanism of authorization remains consistent, but the data transport changes.

Common Pitfalls

Beyond implementation nuances, several specific pitfalls frequently undermine the security of OAuth 2.0 and JWT integrations. First is Token Confusion, where an attacker exploits a server's ability to accept multiple algorithms (e.g., switching from RS256 to HS256) to forge tokens with a known secret. Second is Insecure Storage, specifically saving tokens in localStorage or sessionStorage in client-side applications, which leaves them vulnerable to XSS attacks. Third is Weak Algorithm Enforcement, where developers fail to explicitly whitelist the expected algorithm in the alg header, allowing fallback to insecure defaults or none algorithms. Addressing these requires strict configuration of the JWT library and rigorous testing of the validation logic.

Practical Takeaways

To navigate the choice between opaque tokens and JWTs effectively, consider these three mental models. First, treat Revocation as a state management problem: if you need instant, centralized revocation, choose opaque tokens. Second, treat Latency and Scalability as the primary drivers for JWTs: if your architecture relies on stateless, horizontally scaled microservices that cannot tolerate the overhead of introspection calls, JWTs are the logical choice. Third, treat Complexity as the deciding factor: opaque tokens simplify the resource server but complicate the authorization server's state management, while JWTs shift complexity to the resource server's validation logic and revocation handling.

FAQ

Q: Can I use JWTs with OAuth 2.0? A: Yes. OAuth 2.0 is the protocol that manages the flow, and JWT is a standard format (RFC 7519) often used to carry the access token within that protocol. The protocol does not mandate the token format, allowing for either JWT or opaque tokens.

Q: Why would I choose opaque tokens over JWTs? A: You should choose opaque tokens if immediate revocation is a critical requirement. Since the token itself contains no validation data, the Resource Server must check with the Authorization Server for every request, ensuring that any revocation action is instantly effective.

Q: Is it safe to store JWTs in localStorage? A: Generally, no. Storing JWTs in localStorage exposes them to Cross-Site Scripting (XSS) attacks where malicious scripts can read the storage. For web applications, HttpOnly cookies are the recommended secure storage mechanism, provided CSRF protections are also implemented.

Conclusion

In summary, OAuth 2.0 is the protocol that orchestrates the exchange, and JWT is a container for the data. Confusing the two leads to architectural decisions that prioritize one over the other without understanding the underlying security mechanisms. Whether you choose opaque tokens or JWTs depends on your need for centralized revocation versus distributed scalability. The critical takeaway is that the security of the system relies not on the format itself, but on the rigorous validation of the claims and the enforcement of the protocol's boundaries.

Related posts