📣 Requestly – Modern git-based API Client. No login required. Switch from Postman in 1 click. Try now ->

OAuth vs JWT: What’s the Difference?

Ronak Kadhi
oauth 2.0 is a protocol for getting a token; jwt is a token format with header, payload, and signature — different layers, not rivals

Your lightweight Client for API debugging

No Login Required

Get Requestly

“OAuth vs JWT” is one of the most-searched API security comparisons, and it rests on a category error. The two are not competing choices — they answer different questions. Asking whether to use OAuth or JWT is like asking whether to use “shipping” or “cardboard boxes.” OAuth is a framework for delegating access; JWT is a format for the token that gets delivered. You very often use both at once.

This guide untangles the confusion: what each one actually is, why they keep getting compared, where they overlap, and when you would use a JWT without OAuth or OAuth without JWTs. Then it shows how to inspect and test a JWT-bearing request in the Requestly API client. It is part of our wider guide to API authentication methods.

The short answer

OAuth 2.0 is a protocol for authorization. It defines how an application obtains permission to access resources on a user’s behalf — the redirects, the consent screen, the token endpoint, the scopes. It says nothing about what the resulting token looks like inside.

JWT is a token format. It defines how to encode claims (facts about the subject) into a compact, signed string. It says nothing about how that token was issued or who is allowed to use it.

So the honest comparison is not “OAuth vs JWT.” It is “OAuth, which may issue a JWT” and “JWT, which may or may not have come from OAuth.” They operate at different layers.

What OAuth 2.0 actually is

OAuth 2.0 (RFC 6749) is a delegated authorization framework. Its job is to let a user grant a third-party application scoped, revocable access to their data without handing over their password. The user authorizes the app at an authorization server; the app receives an access token; the app presents that token to the API.

The defining features of OAuth are the things around the token: the grant flows (Authorization Code, Client Credentials, Device Code), scopes that limit what the token can do, refresh tokens for renewal, and central revocation. The access token itself is treated as opaque by the client — the client does not need to read it, only to present it. (For the flows in detail, see OAuth 2.0 Flows Explained.)

What a JWT actually is

A JSON Web Token (RFC 7519) is a way to represent claims as a signed, URL-safe string. It has three Base64URL-encoded parts separated by dots: header.payload.signature.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJzdWIiOiIxMjM0Iiwicm9sZSI6ImFkbWluIiwiZXhwIjoxNzM1Njg5NjAwfQ
.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Decoded, the payload is just JSON:

{
  "sub": "1234",
  "role": "admin",
  "exp": 1735689600
}

The signature lets the recipient verify two things without a database lookup: that the token was issued by a trusted party (it holds the signing key) and that the claims have not been tampered with. That self-contained, stateless verification is the entire appeal of JWTs.

Critical caveat: a JWT is signed, not encrypted (unless you specifically use JWE). Anyone can decode the payload — it is Base64, not a secret. Never put passwords or sensitive PII in a plain JWT, and always verify the signature server-side. A token you accept without checking the signature is worthless.

Why they get compared (and where they overlap)

The confusion is understandable: in a very common setup, an OAuth 2.0 authorization server issues access tokens that happen to be JWTs. The API then validates the JWT’s signature locally instead of calling back to the auth server on every request. So in that architecture, “OAuth” and “JWT” describe the same token from two angles — OAuth is how it was obtained, JWT is what it is made of.

But the two come apart cleanly:

  • OAuth without JWT. Many providers issue opaque access tokens — random strings with no internal structure. The API validates them by calling the auth server’s introspection endpoint. This is still fully OAuth; there is no JWT anywhere.
  • JWT without OAuth. A monolithic app can issue its own JWT at login, sign it, and accept it on subsequent requests — no authorization server, no third party, no OAuth flow. Plenty of first-party APIs do exactly this.
OAuth 2.0JWT
What it isAuthorization frameworkToken format
AnswersHow is access granted and delegated?How is a token encoded and verified?
Defines flows / redirectsYesNo
Defines token structureNo (token is opaque to the client)Yes (header.payload.signature)
Stateless verificationNot inherentlyYes, via signature
RevocationBuilt in (refresh + revoke)Hard — valid until it expires

The trade-off that actually matters: revocation vs statelessness

If you understand one practical difference, make it this one. A self-contained JWT is valid until its exp claim passes, because verification is just a signature check — the server does not consult any store. That is fast and scales beautifully, but it means you cannot easily revoke a JWT. If a token leaks, it stays valid until expiry unless you build extra machinery (a deny-list, short lifetimes plus refresh, or key rotation).

OAuth’s token lifecycle is designed for exactly this: access tokens are short-lived, refresh tokens can be revoked, and the authorization server is the single point of control. The common production pattern marries both — OAuth issues short-lived JWT access tokens (fast stateless validation) backed by revocable refresh tokens (control). You get statelessness for the 15-minute window and revocation at the refresh boundary.

Inspect any token you hold: Paste a JWT into a request’s Authorization tab in Requestly, fire it, and assert on the response — no scratch scripts, no online decoders that send your token to a stranger. Explore the API client →

When to use which

Frame it as two separate decisions, not one.

Do you need OAuth 2.0? Use it when a third party must act on a user’s behalf with scoped, revocable access, or when you want a central authorization server across many services. If it is your own monolith authenticating your own users, you may not need the full framework — this is the genuine decision, and we cover the related key-vs-OAuth choice in API Key vs OAuth.

Should your tokens be JWTs? Choose JWTs when stateless, distributed verification matters — many services validating tokens without a shared session store. Choose opaque tokens when you want easy, immediate revocation and are comfortable with an introspection call or a shared session lookup.

These are orthogonal. You can run OAuth with opaque tokens, OAuth with JWTs, or plain JWTs with no OAuth at all.

Testing a JWT-bearing request in Requestly

However the token was issued, it almost always rides on the request as a Bearer credential, and that is what you test. In Requestly:

  1. Open the request and go to the Authorization tab.
  2. Select Bearer Token and paste the JWT — or, better, reference an environment variable so it is not hardcoded.
  3. Send the request and confirm the API accepts it.

Keep the actual token in an environment variable (or the Vault, which integrates with AWS Secrets Manager) rather than pasting it into a saved request:

Authorization: Bearer {{jwt}}

If you get the JWT from a login endpoint, capture it automatically with a post-response script so the rest of your collection reuses it. Note that a Requestly script reads the response it is attached to — it does not make its own HTTP calls — so this runs on the login request itself:

// Post-response script on the login request.
// Adjust the field name to match your API's response.
const data = rq.response.json();
rq.environment.set("jwt", data.token);

rq.test("Got a JWT", function () {
  rq.expect(rq.response.code).to.equal(200);
  rq.expect(data.token).to.be.a("string");
});

You can also assert on the decoded claims that the API returns, or simply confirm a protected route accepts the token and an expired one does not:

rq.test("Token is accepted", function () {
  rq.expect(rq.response.code).to.not.equal(401);
});

Because Requestly is local-first, the JWT stays on your machine instead of being pasted into a third-party web decoder — which matters, since a JWT is a live credential until it expires. If your token came out of a full OAuth flow, the configuration steps are in OAuth 2.0 Flows Explained, and the full Bearer lifecycle is in How to Use Bearer Tokens for API Authentication.

Stop pasting tokens into random websites: Test JWT-bearing requests locally, store the token in the Vault, and assert it is accepted — all in Requestly. Explore the API client →

Frequently asked questions

Is it OAuth or JWT — which one should I choose?

It is not an either/or choice. OAuth 2.0 is a framework for granting and delegating access, while JWT is a format for the token that gets issued. You often use both together: an OAuth authorization server can issue access tokens that happen to be JWTs. Decide whether you need OAuth, and separately whether your tokens should be JWTs.

Can you use JWT without OAuth?

Yes. A first-party application can issue its own signed JWT at login and accept it on later requests with no authorization server and no OAuth flow involved. This is common for monolithic apps that authenticate their own users and do not need third-party delegation.

Can OAuth work without JWTs?

Yes. Many OAuth providers issue opaque access tokens, which are random strings with no internal structure. The API validates them by calling the authorization server’s introspection endpoint. This is fully OAuth with no JWT anywhere in the system.

Is a JWT encrypted?

No. A standard JWT is signed, not encrypted, so anyone can Base64-decode and read its payload. The signature only guarantees the token was issued by a trusted party and has not been altered. Never store sensitive data in a plain JWT, and always verify the signature on the server.

Why are JWTs hard to revoke?

A self-contained JWT is valid until its expiry claim passes, because the server verifies it with a signature check rather than a database lookup. There is no central record to invalidate, so revoking one early requires extra machinery such as a deny-list, very short lifetimes with refresh tokens, or rotating the signing key.

How do I test a request that uses a JWT?

In Requestly, open the request, choose Bearer Token in the Authorization tab, and reference the JWT from an environment variable instead of hardcoding it. Send the request and add an assertion such as rq.expect(rq.response.code).to.not.equal(401) to confirm the token is accepted by the API.

Written by
Ronak Kadhi

Get started today

Join 300,000+ developers building smarter workflows.
Get Started for Free
Contact us