You’ve probably seen JWT and OAuth mentioned together in tutorials, libraries, or other tech blogs. They’re often treated like two options for solving the same problem, but they’re not and that’s where a lot of the confusion starts.
And while they often work together, they do very different things.
So if you’re trying to figure out which one to use, when to use it, or if you need both, you’re in the right place.
In this guide, I’ll break down what JWT and OAuth actually do, how they fit into real-world apps, when to use them, and what to avoid along the way so you can make confident decisions when building your own projects.
Let's get into it…
JWT stands for JSON Web Token. It’s a compact, URL-safe string used to pass trusted information between systems. Most often, that information is about who someone is — like a user ID, role, or other identity claim.
You’ll usually see JWTs come into play after someone logs into your system. The server creates a token, includes some relevant data in it, signs it, and sends it to the client. From there, the client can include that token with each request. No session needed — the token carries the identity info.
Each JWT has three parts:
What makes JWTs especially useful is that they’re stateless. This means that you don’t need to store anything on the server because every request already includes the info needed to validate it.
That’s a big win for scalability. Especially in systems that are spread across multiple services or servers.
One thing to keep in mind though is that JWTs aren’t encrypted by default, they’re just encoded. That means anyone who intercepts a token can read what’s inside, even if they can’t change it. So never store anything sensitive in the payload, and always send tokens over HTTPS to keep them secure.
So, when does it actually make sense to use JWTs? Let’s walk through a few places where they really shine.
Let’s say you’re building a single-page app (SPA) that talks to a backend API. When someone logs in, the server sends back a signed JWT. From then on, every API call includes that token. The backend checks the signature, verifies its legit, and processes the request — no database lookup, no session tracking, and no server-side memory to manage.
This approach works especially well when your frontend and backend are decoupled or deployed separately which is pretty common these days.
JWTs are also handy when your system is made up of several services. Maybe one handles billing, another handles content, and another handles user profiles.
If each service knows how to verify the signature, they can all trust the same token. That means no central session store, no extra network hops, and no need to sync login state across services. Each one can make its own decisions based on the same token, which keeps your architecture simple and scalable.
JWTs aren’t just for IDs. You can also include useful claims like user roles, feature flags, or language preferences. Basically, anything small that helps your services respond the right way without needing an extra database lookup.
That said, remember what we covered earlier: JWTs aren’t encrypted by default. They’re readable by anyone who gets ahold of them, even if they can’t modify them. So avoid storing sensitive or private data in the payload. Think of JWT as a way to pass around public-but-trusted info that helps your system make decisions faster.
Being able to carry that kind of context with each request can reduce complexity and make your system more responsive, especially when multiple services are involved.
JWTs work best when you need simple, stateless, scalable authentication — especially in systems made up of APIs, services, or components that all need to trust the same identity without constant database checks or shared memory.
Now that you’ve got a handle on JWT, let’s talk about where OAuth fits in — and why these two often get confused with each other.
Despite what you might’ve heard, OAuth isn’t a login system. It’s an authorization framework. Instead of proving who someone is, OAuth is about granting permission — letting one service do something on someone’s behalf, without handing over a password.
You’ve probably used it without realizing. Clicked “Continue with Google” or “Sign in with GitHub”? That’s OAuth. You get redirected, log in with the provider, approve the request, and the app gets a token saying: “This user is verified, and here’s what they’ve agreed to let you do.”
That might mean the app can see your email address — but not your calendar. Or maybe it can access your calendar, but not your contacts. OAuth makes that kind of scoped, temporary access possible.
Another example: say you’re using Buffer to schedule tweets. Instead of entering your Twitter password into Buffer, you click “Connect to Twitter,” approve the request, and Twitter sends back a token that lets Buffer post tweets — but not delete them or access your DMs. You stay in control.
Here’s how OAuth works behind the scenes:
The flow looks like this:
The client asks for access → the authorization server checks with you → you approve → the client gets a token → the client uses that token to access your data.
This setup makes OAuth a natural fit for anything that involves third-party access — integrations, mobile apps, internal tools, or cloud platforms. And because OAuth tokens can expire or be revoked, it gives users and developers more control over what’s shared, and for how long.
So when does OAuth actually make sense for something you’re building?
The short answer: any time your app needs to act on a user’s behalf — especially when that means talking to another service, limiting permissions, or giving the user control over what’s shared.
For example
Lets say you’re building a productivity tool that syncs with Google Calendar.
You don’t want to ask users for their Google password — and they shouldn’t give it to you anyway. With OAuth, you send them to Google, they log in, approve your app, and you get a token that says, “This app can manage this calendar — and nothing else.”
Or maybe you’re working on a budgeting app that connects to a user’s bank account. OAuth lets the user grant access just to their transaction history — not their login, not their credentials, and not their ability to move money.
OAuth is also the go-to in enterprise systems. Maybe your dashboard needs to pull Salesforce data, post to Slack, or read files from Google Drive. OAuth lets you do that securely — no passwords, no stored credentials, no brittle API keys.
Here’s when OAuth is probably the right tool:
Just remember: OAuth isn’t a replacement for authentication in your own app.
If you’re just managing your own users and don’t need to connect to outside systems, OAuth can add unnecessary complexity. But when you do need secure, temporary, and scoped access across systems, it’s built exactly for that.
Now that you’ve seen how JWT and OAuth work individually, it’s easier to understand why they get confused so often, and why they show up together in so many projects.
Here’s where the mix-up usually comes from:
So how do you keep them straight? One is a format (JWT), and the other is a framework (OAuth). They work together often — but they’re solving different problems.
Let’s make that crystal clear with a side-by-side breakdown:
Feature | JWT | OAuth |
---|---|---|
What it is | A token format for securely transmitting data | An authorization framework for granting access |
What it does | Identifies and validates who someone is | Grants access to specific data or actions |
Used for | Stateless authentication between systems | Delegated access between apps or services |
Token type | Always a JWT | Can be a JWT, an opaque token, or another format |
Needs a third-party? | No — can be used entirely within your own system | Often — typically involves another service or identity provider |
Example scenario | A user logs in and gets a token to call your API | An app requests access to a user's Google Calendar |
Do they work together? | Yes — JWT is often used within OAuth as the access token | Yes — OAuth often issues JWTs to represent access rights |
By now, you’ve seen how JWT and OAuth each solve different problems. But in real-world projects, it’s easy to misuse them — especially when their roles aren’t clearly understood.
Here are the most common mistakes you’ll want to avoid, and how to fix them the right way.
It’s easy to fall into the trap of thinking you can use a JWT for any kind of authentication, including when your app needs to talk to other services.
But JWTs are really just a format. They don’t handle consent, permissions, or token exchange. That’s OAuth’s job.
This mistake usually shows up when you're building integrations or trying to connect to third-party APIs. You already know how JWT works inside your own system, so you try to use it to call out to another service.
But if that service doesn’t recognize your token — or never issued it in the first place — the request will fail. Or worse, you’ll build a workaround that skips proper access control.
For example
Let’s say you’re building a productivity app and want to pull events from someone’s Google Calendar. You might think, “I already issued a JWT when they logged in, so I’ll just use that to authenticate the request to Google.”
But Google doesn’t accept your tokens because it needs to issue its own, based on a flow the user approved.
And so trying to handle that with a JWT of your own leads to brittle code or risky shortcuts. You’re bypassing the very thing OAuth is built to manage: trust between systems.
What to do instead
When your app needs to act on behalf of a user, especially when talking to systems you don’t control, use OAuth.
And sure, usually that token will be a JWT, but it’ll be issued by the right party, with the right guarantees.
It’s tempting to treat a JWT like a trusted data source. After all, it’s signed, and it came from your login flow so why wouldn’t it be safe to use?
But here’s the problem…
If your backend doesn’t verify the JWT’s signature before reading its contents, then anyone can forge a token. That includes attackers who copy the format, tweak the payload, and send it along, hoping your app just takes their word for it.
For example
Say your JWT includes "role": "admin"
, and your front-end uses that to show admin features. If the server blindly trusts the token without verifying its signature, someone could craft a fake token with the same structure and assign themselves admin rights. Your app has no idea the data wasn’t issued by you.
This kind of vulnerability often shows up when teams confuse authentication with authorization, assuming that just having a token is enough to prove someone’s identity or access level.
What to do instead
Always verify the JWT’s signature before trusting any data inside. If the token fails signature validation — whether it’s signed with the wrong secret or tampered with in any way — reject it.
Also, never rely on payload values like roles or scopes unless you’re sure they came from your system or a trusted provider.
One of the most common misconceptions about JWTs is that they’re secure by default — but they’re not. They’re signed to prevent tampering, not encrypted to keep their contents secret.
That means anyone who gets hold of a token can open it up and read what’s inside. It might look like a long, unreadable string at first, but it’s just Base64-encoded. Anyone with a browser dev tool or online decoder can see the full payload in seconds.
For example
Imagine you include an email address, internal user ID, and a few feature flags in the payload. It might seem harmless. After all, it’s just data your app already knows.
But now that info is visible to browser extensions, third-party scripts, and anyone who logs requests. It’s even worse if you include something like a password hash or an internal admin key — suddenly you’ve got a serious data exposure on your hands.
This mistake usually happens when devs try to reduce backend lookups by stuffing too much context into the token. But JWTs aren’t meant to be a storage mechanism. They’re meant to pass public-but-trusted identity information.
What to do instead
Only include data in a JWT that you’d be fine showing to the user directly. If something needs to stay private — like personal info, credentials, or sensitive access rights — store it server-side. And always send JWTs over HTTPS to prevent them being intercepted in the first place.
JWTs are stateless, which makes them fast and scalable, but it also means you can’t revoke them once they’ve been issued. There’s no server-side session to destroy, no logout event that invalidates the token. If a JWT is valid, it’s valid until it expires, no matter what happens in between.
That’s fine for low-risk apps. But the longer your tokens live, the more damage they can do if they’re ever compromised — especially in systems that handle financial, medical, or enterprise data!
For example
Imagine your app issues a 7-day token. A user logs in, gets their token, and everything works smoothly. Up until they lose their laptop, or someone grabs the token from localStorage
. That token will continue to work for the full seven days, unless you’ve built your own blacklist or revocation system.
You now have a ghost session that looks legitimate, but shouldn’t exist anymore.
What to do instead
Keep access tokens short-lived. Somewhere between 15 minutes and an hour is usually safe. Then use refresh tokens to maintain longer sessions in a controlled way.
This gives you more chances to detect issues, rotate tokens, or force reauthentication when needed, without sacrificing user experience.
Hopefully this guide has helped clear up those common confusions!
As you can see, although JWT and OAuth get bundled together, they’re actually solving different problems.
The reality is you don’t have to choose one or the other. A lot of systems use both JWT for managing user sessions and OAuth for handling external permissions. The trick is just understanding when to use each.
If you’re ready to try it yourself, start small with JWT for login, then add OAuth when your app needs to reach beyond itself.
If you enjoyed this post, then check out my other guides and tutorials!