JWT vs OAuth: What's the Difference and When to Use Each
Ask five developers how JWT and OAuth relate, and you may well get five different answers. Some treat them as interchangeable; others describe them as competing standards, one to be chosen over the other. Neither view is accurate, and the confusion shows up in code reviews, architecture documents, and interview questions alike.
The short version: JWT is a data format, and OAuth is an authorization process. They solve different problems, and in most production systems they work together rather than replacing each other. This guide explains what each one actually does, where they overlap, and how to think about them when you're securing an API.
JWT vs OAuth: They're Not Actually Competing Technologies
JWT (JSON Web Token) is a compact, signed data structure for representing claims: a way to package information so it can be verified. OAuth is an authorization framework: a defined sequence of steps for granting an application limited access to a resource. JWT describes what a token looks like, while OAuth describes how a client gets one in the first place.
That distinction matters because the two are usually deployed side by side, not chosen between. Comparing them directly is a bit like comparing an envelope to a postal system. OAuth defines the handshake that gets a client an access token, and JWT is one of the formats that token can take once it's issued.
What Is JWT?
A JWT is a self-contained token: a JSON object containing claims (statements about a user, a client, or a set of permissions), digitally signed so a recipient can verify it hasn't been tampered with. RFC 7519, the IETF standard behind the format, is built around exactly that idea: a compact, URL-safe container for claims that can be wrapped in a digital signature or an encryption layer, so the receiving party can trust what's inside.
The signature is what makes a JWT "self-contained." A service that receives one can check the signature locally, using a shared secret or a public key, and trust the claims inside without calling back to the system that issued it. That's a very different operating model from a database-backed session token, which needs a lookup on every request.
It's worth being precise here, though. A valid signature only proves the token hasn't been altered since it was issued. It doesn't, on its own, mean the claims are acceptable to the API checking them. The resource server still has to validate the expected issuer, audience, expiration, and any required scopes before granting access. RFC 8725, the JWT Best Current Practices document, walks through exactly this class of validation mistake.
JWT structure (header, payload, signature)
The three-part header.payload.signature layout described below is the signed form of a JWT (a JWS-based token). An encrypted JWT, which uses JWE instead of JWS, is serialized differently, as five dot-separated parts rather than three. The signed form is by far the more common case for access tokens and ID tokens, so it's what the rest of this guide assumes unless noted otherwise.
- Header: names the signing algorithm and token type, for example
{"alg": "RS256", "typ": "JWT"}. - Payload: the claims themselves, such as the issuer (
iss), subject (sub), expiration (exp), and any custom claims the application needs, like roles or scopes. - Signature: computed over the header and payload using the algorithm named in the header, so any change to either invalidates the signature.
Here's what a fictitious access token looks like when decoded. The header:
{ "alg": "RS256", "typ": "JWT" }
And the payload:
{
"iss": "https://auth.example.com",
"sub": "user_8841",
"aud": "https://api.example.com",
"exp": 1735689600,
"scope": "read write"
}
A resource server validating this token has to check more than the signature. Does iss match a trusted issuer? Does aud match this particular API? Has exp passed? Does scope cover what the request is trying to do? A token can pass signature verification perfectly and still fail every one of those checks, for example if it was issued for a completely different API. Skipping any of them is a common source of real vulnerabilities, not a theoretical one, which is why RFC 8725 exists.
A decoded JWT is plain, readable JSON. It isn't encrypted by default, so it shouldn't carry secrets: the signature guarantees integrity, not confidentiality. If a token needs to stay confidential in transit or at rest, that requires JWE (JSON Web Encryption) on top, or simply keeping sensitive data out of the claims altogether. A tool like jwt.io is a common way to decode and inspect a token's header and payload while debugging.
What Is OAuth?
OAuth 2.0 is an authorization framework. According to RFC 6749, it exists to let a third-party application obtain scoped access to an HTTP service, either through an approval step involving the resource owner or, in some flows, without one at all. The core idea is delegation. Instead of handing your password to every app that needs to act on your behalf, you approve a narrower, revocable grant of access, and the app receives a token that proves it.
That delegation involves four roles defined in the spec:
- Resource owner: usually the user.
- Client: the app requesting access.
- Authorization server: issues tokens after checking the client's request.
- Resource server: the API that accepts the token.
Whether OAuth ends up using a JWT is a separate decision. The framework doesn't mandate a token format at all. The access token can be an opaque string that means nothing outside the issuing server, or it can be a self-contained JWT.
A related, newer effort is worth knowing about. OAuth 2.1 consolidates several security practices that grew up around OAuth 2.0 as recommendations, most notably requiring PKCE (Proof Key for Code Exchange) for the authorization code flow and dropping the implicit and password grants. At the time of writing, OAuth 2.1 is still an IETF Internet-Draft rather than a published RFC, so treat it as the direction the ecosystem is heading, not as a finished standard.
How OAuth's authorization flow works
The most common flow, the authorization code grant, runs roughly like this:
- The client redirects the user to the authorization server.
- The user authenticates and approves the requested access.
- The authorization server redirects back with a short-lived authorization code.
- The client exchanges that code for an access token at a back-channel token endpoint.
How that exchange is authenticated depends on the type of client. A public client, such as a single-page app or mobile app that can't safely hold a shared secret, uses PKCE to prove it's the same client that started the flow. A confidential client, such as a server-side app, authenticates with its configured credentials, usually alongside PKCE as well. The access token is then attached to API requests, typically in an Authorization: Bearer <token> header, a pattern standardized separately in RFC 6750.
OAuth also defines other grant types for different situations. These include client credentials for machine-to-machine calls with no end user involved, refresh tokens for extending access without prompting the user again, and JWT-based grants (covered below) for systems that already hold a signed assertion. Which one fits depends on who's asking and what they already have.
Key Differences Between JWT and OAuth
| Dimension | JWT | OAuth |
|---|---|---|
| What it is | A token format: a signed JSON data structure | An authorization framework: a defined flow of steps |
| Primary purpose | Represent claims in a verifiable, portable way | Delegate limited access from a resource owner to a client |
| Statefulness | Self-contained; can be validated without a server-side lookup | Involves stateful steps (authorization codes, refresh tokens) even when the resulting access token is stateless |
| Typical use case | API authentication tokens, ID tokens, internal service-to-service claims | Third-party app access and delegated API access (user sign-in and SSO run on top of OAuth through OpenID Connect, not OAuth alone) |
| Complexity to implement | Low: generate and verify a signed token | Higher: requires an authorization server, defined grant flows, and token lifecycle management |
The practical takeaway from this table is that "JWT or OAuth, which is better?" is the wrong question, because the two are rarely alternatives for the same job. A more useful question is what the access token issued by an OAuth flow should actually look like, and that's usually where JWT comes in.
How JWT and OAuth Work Together
This is the part that clears up most of the confusion. OAuth 2.0 doesn't require any particular token format; RFC 6749 leaves that choice to the implementation. In practice, a large share of OAuth deployments issue JWTs as access tokens, because a self-contained, signed token lets resource servers validate requests without calling back to the authorization server every time.
The flow looks like this. A client completes an OAuth grant, such as the authorization code flow described above, and the authorization server issues an access token formatted as a JWT. When an API request arrives, the resource server checks the JWT locally, validating its signature and confirming its claims: expiration, intended audience, and required scopes. No round trip to the authorization server is needed unless the token must be revoked before it expires. That's the standard trade-off with self-contained tokens: faster validation, in exchange for less immediate control over early revocation.

WSO2 API Platform's gateway is a concrete example of this pattern in production. Its JWT Authentication policy validates self-contained access tokens locally against the issuer's published keys (JWKS). It checks issuer, audience, and scopes, allows only RS256, PS256, or ES256 signatures, and rejects HMAC and none outright, so no call back to the authorization server is needed on each request. Where an authorization server issues opaque tokens instead, the Opaque Token Authentication policy validates them through RFC 7662 token introspection. That's the output-format side of the pattern: OAuth handles delegation, and the resulting access token may or may not be a JWT.
A JWT can also play a different role in OAuth, as the input to a token request rather than the output. The JWT Bearer Grant (RFC 7523) lets a client exchange a signed JWT assertion, often issued by a trusted external identity provider, for an OAuth 2.0 access token without fresh end-user approval each time. In that case the JWT is the authorization grant itself. It doesn't determine the format of the resulting access token, which is governed separately by the authorization server's access-token policy.
One interoperability detail is worth knowing if you're integrating across identity systems. When a JWT is used as an OAuth access token, the standard format for its scope claim, a space-separated string, comes from RFC 9068 (the JWT profile for OAuth 2.0 access tokens), which refers to the claim definition in RFC 8693 (OAuth 2.0 Token Exchange). Some legacy applications and downstream services expect scopes as a JSON array instead. That's a departure from the standard profile, not an equivalent format, so a consumer has to be explicitly configured to parse it. It's a small detail, but it's exactly the kind that surfaces the moment you connect systems that grew up with different conventions for the same OAuth concept.
When to Use JWT vs OAuth
Because they answer different questions, the choice usually isn't "JWT or OAuth." It's two separate decisions.
Reach for OAuth when:
- A third-party application needs limited, revocable access to a user's data or to an API, without ever seeing the user's credentials.
- You need a standard, interoperable way for a client to prove it has been granted specific scopes.
Reach for OpenID Connect (built on OAuth) when:
- You're implementing single sign-on or need to verify who a user is, not just what they're allowed to access. OAuth on its own was designed for authorization, not authentication, so plain OAuth access tokens aren't reliable proof of identity. OpenID Connect adds a standardized ID token, which is a JWT, that the client uses to establish who has signed in. It's kept separate from the access token the client presents to APIs.
Reach for JWT when:
- You need a token that a resource server can verify without a network call to an authorization server, which is useful for high-throughput APIs and distributed services.
- You're passing identity or claims data between internal services that trust a common signing key.
- You need a self-describing token that carries its own expiration, audience, and permission claims, rather than acting as a lookup key into a session store.
Combine JWTs and session-based tokens carefully. An opaque, database-backed session token gives you instant revocation and simpler auditing, at the cost of a lookup on every request. A JWT gives you fast, stateless validation, at the cost of staying valid until it expires unless you add a revocation mechanism (a short expiry plus refresh tokens is the usual compromise). Neither is universally right. The choice depends on how much you value instant revocation compared with avoiding a database round trip on every API call, and many production systems end up using both: short-lived JWTs for API calls, and a revocable session for the browser itself.
How WSO2 Implements JWT and OAuth Together
WSO2's API Platform is built around the pattern described above. OAuth 2.0 handles the delegation and grant logic, and JWT is the default format for the resulting access token. A few specifics are worth naming:
- Self-contained JWT access tokens. The gateway's JWT Authentication policy validates signed, self-contained access tokens locally against the issuer's JWKS, without a database round trip on every call. Tokens can come from the built-in API Platform STS, which is for client (application) authentication only, or from an external identity provider such as Asgardeo or Azure AD when end-user authentication is needed. That's a real architectural trade-off, not a universal win: it buys lower per-request latency in exchange for the same early-revocation caveat that applies to self-contained tokens in general.
- Opaque tokens when you need them. For authorization servers that issue reference tokens, the Opaque Token Authentication policy validates them through RFC 7662 introspection instead. It trades a briefly cached introspection call for revocation that the gateway sees within that cache window.
- MCP traffic. The same JWT validation underpins the AI Gateway's MCP Authentication policy, which also serves OAuth protected resource metadata so MCP clients can discover which authorization server to use.
The same delegation pattern extends beyond human users. When an AI agent calls tools through the AI Gateway's MCP Proxy, the MCP Authorization policy reads the claims and scopes in the validated JWT to decide, for each tool, resource, or prompt, whether the call is allowed. It's the same JWT-plus-OAuth shape described throughout this guide, applied to a newer kind of client.
None of this changes what JWT or OAuth are. It's one current, verifiable example of the general pattern this guide describes: an authorization framework issuing a self-contained token format as its credential of choice. WSO2's API Platform documents the supported identity providers, token validation policies, and their configuration in more depth, for teams evaluating how the pieces fit together in their own architecture.
Conclusion
JWT and OAuth get treated as rivals because they're so often mentioned in the same breath, but the comparison falls apart once you separate what each one does. JWT defines a token's shape. OAuth defines how a client earns the right to hold one. Most real systems, including the pattern implemented across WSO2's platform, use both together: an OAuth flow handles delegation, and a signed JWT carries the resulting credential.
If you're deciding how to secure your own APIs, the useful question isn't which one to choose. It's whether your access tokens need to be self-contained and independently verifiable, whether your authorization flow needs the delegation model OAuth provides, or both. For teams evaluating how that works in a production gateway and identity setup, WSO2's API Platform is a good place to see the JWT-and-OAuth pattern applied end to end.
Frequently Asked Questions
Is JWT the same as OAuth?
No. JWT is a token format, a signed way of representing claims. OAuth is an authorization framework, a set of flows for granting delegated access. OAuth often uses JWT as its access token format, but neither is a substitute for the other.
Why use JWT instead of Basic Auth?
Basic Authentication sends a username and password (base64-encoded, not encrypted) with every request, so credentials are exposed again and again, and there's no built-in way to scope or expire access. A JWT is issued once, carries its own expiration and permission claims, and can be verified without sending a password on each call.
Can OAuth and JWT be used together?
Yes, and it's one of the most common patterns in production API security. OAuth governs how a client obtains an access token, and that access token is frequently formatted as a JWT so resource servers can validate it locally.
Why is it a bad idea to use OAuth 2.0 for authentication?
OAuth 2.0 was designed for authorization, granting access to a resource, not for verifying who a user is. Using an OAuth access token as a stand-in for user identity leaves gaps around audience validation and identity assurance. OpenID Connect (OIDC) was built on top of OAuth 2.0 specifically to close that gap, adding a standardized ID token and identity claims. If you need authentication, OIDC is the standard to use, not raw OAuth 2.0.