OAuth 2.0 vs JWT Explained: Authentication vs Authorization (2026 Guide)
This is a PerfectNotes study guide β also known as PN Notes or Perfect Notes. PerfectNotes provides free computer science student notes, MCQs, and interview preparation guides at perfectnotes.org.
Key Takeaways & Definition
- Definition: OAuth 2.0 is an authorization framework (RFC 6749); JWT is a signed token format (RFC 7519). They are not alternatives β OAuth 2.0 uses JWTs as its access tokens.
- AuthN vs AuthZ: Authentication proves WHO you are (passport check). Authorization proves WHAT you can do (key card at the room door).
- JWT is NOT encrypted: The Payload is base64-encoded only β anyone with the token can read all claims. Never store passwords or PII in JWT claims.
- RS256 over HS256 in microservices: asymmetric signing ensures only the Auth Server mints tokens; Resource Servers verify via the public JWKS endpoint.
- PKCE is mandatory for all public clients (SPAs, mobile apps) in OAuth 2.1. The Implicit Flow is deprecated.
- Two-token strategy: Short-lived Access Token in memory + long-lived Refresh Token in an HttpOnly cookie + Refresh Token Rotation = the secure production default.
- OIDC extends OAuth 2.0 with an ID Token, turning pure authorization into full identity β powering every "Login with Google" flow.
OAuth 2.0 is an authorization framework β it grants third-party apps limited access to a user's resources without sharing passwords.
JWT (JSON Web Token) is a stateless token format β it encodes user identity and claims as a signed, verifiable string.
OAuth 2.0 uses Access Tokens (short-lived) and Refresh Tokens (long-lived) to manage API authorization securely.
JWT contains three Base64-encoded parts: Header (algorithm), Payload (claims), and Signature (verification).
Never store JWTs in localStorage β use httpOnly cookies to prevent XSS token theft attacks.
Digital Identity: The Two Questions Every System Must Answer
Every time you access a protected resource β your email inbox, a banking API, a GitHub repository β two distinct questions are asked in sequence:
- WHO are you? β Prove your identity. (Authentication)
- WHAT are you allowed to do? β Check your permissions. (Authorization)
These are fundamentally different problems, but they are frequently confused β even in senior engineering interviews and architecture reviews. OAuth 2.0 and JWT are the dominant solutions to the second problem, but they cannot operate without the first.
This guide demystifies both concepts from first principles: what they do, how they interact, and the exact security tradeoffs you must understand to design production systems.
Authentication vs Authorization β The Most Misunderstood Distinction in Security
Developers conflate these terms so frequently that most companies explicitly add them to interview rubrics. The distinction is architectural, not cosmetic.
Authentication (AuthN) is the process of verifying identity. It answers: "Are you really who you claim to be?" Mechanisms include passwords, biometric scans, hardware security keys (FIDO2/WebAuthn), and cryptographic certificates.
Authorization (AuthZ) is the process of granting or denying access to specific resources. It answers: "Given that I know who you are, what are you permitted to do?" Mechanisms include RBAC (Role-Based Access Control), ABAC, scopes, and JWT claims.
The Hotel Key Card Analogy
Imagine checking into a hotel. The entire process maps perfectly to modern authentication and authorization:
| Hotel Step | Authentication (AuthN) | Authorization (AuthZ) |
|---|---|---|
| Arriving at the front desk | You show your passport (identity proof) | β |
| Clerk verifies and issues key card | Identity confirmed β credential issued | Card programmed with room 302 + gym access |
| Tapping key card at room door | β | Door checks permissions, not identity |
| Tapping key card at penthouse suite | β | Access denied β scope not granted |
The critical insight: the key card reader has no idea who you are. It only checks: "does this card have permission to open this door?" That separation is the entire point of OAuth 2.0.
Why We Stopped Sharing Passwords β The Origin of OAuth
Before OAuth, the common pattern for third-party app integration was the "password anti-pattern": you gave a third-party app (say, a calendar app wanting to read your Gmail contacts) your actual Google password. The problems were catastrophic:
- Over-privileged access: The calendar app had full account access, not just contact read access.
- No revocation: To cut off access, you had to change your Google password β breaking every other app too.
- Security liability: The third-party app stored your password β now every breach of that app exposed your Google account.
- No audit trail: Google couldn't distinguish actions taken by you from actions taken by the app.
OAuth 2.0 solved this with delegated authorization: the user grants a specific, limited set of permissions (called scopes) to a specific application, for a specific period of time, without ever sharing their password. Access can be revoked at any time without affecting other applications.
What is OAuth 2.0? The Authorization Framework
OAuth 2.0 defines four key roles:
| Role | Description | Hotel Equivalent |
|---|---|---|
| Resource Owner | The user who owns the data | Hotel guest |
| Client | The app requesting access (your SPA, mobile app, server) | Visitor wanting to enter a room |
| Authorization Server | Issues access tokens after user consent (Google, Okta, Auth0) | Hotel front desk / key card programmer |
| Resource Server | The API that holds the protected data (Gmail API, GitHub API) | Hotel rooms, gym, pool |
The Scopes system is the granularity mechanism: read:contacts, write:calendar, openid, email, profile. A client requests only the scopes it needs (principle of least privilege), and the user must explicitly consent to each one.
What is a JWT? The Self-Contained Access Pass
A JWT (JSON Web Token, pronounced "jot") is a compact, URL-safe token format defined in RFC 7519. Think of it as a VIP pass: it carries all the information needed to make an access decision (your identity, your roles, the expiry time) in a tamper-proof, self-contained package.
The killer feature of JWTs is stateless verification. An API server can validate a JWT by checking its cryptographic signature against the Auth Server's public key β no database call required. This scales horizontally to millions of requests per second.
Why Comparing OAuth 2.0 vs JWT is a Trap β They Work Together
Asking "OAuth 2.0 vs JWT: which should I use?" reveals a category error. One is a protocol framework; the other is a data format. The real comparison would be "OAuth 2.0 vs SAML" (both are authorization frameworks) or "JWT vs opaque tokens" (both are token formats).
In practice, they are designed to work together: OAuth 2.0 defines the flow that issues the token; JWT is the format of that token.
Advanced Engineering Concepts
Production-grade OAuth 2.0 and JWT implementations require a deep understanding of JWT anatomy, cryptographic signing algorithms, PKCE code exchange mechanics, and the two-token security strategy. The following sections cover these implementation-level details required for secure deployments.
JWT Anatomy: Header.Payload.Signature
A JWT looks like a long, opaque string β but it has precise structure. Take this example:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIiwicm9sZSI6ImFkbWluIiwiZXhwIjoxNzEyMDAwMDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c| Part | Content | Purpose | Encrypted? |
|---|---|---|---|
| Header | {"alg":"RS256","typ":"JWT"} | Declares signing algorithm & token type | No β base64only |
| Payload | {"sub":"user123","role":"admin","exp":1712000000} | Carries user claims (sub, iat, exp, roles, scopes) | No β base64only |
| Signature | RSASHA256(base64(header)+"."+base64(payload), privateKey) | Proves the token was not tampered with | Signed (not encrypted) |
Standard JWT claims include: sub (subject/user ID), iss (issuer), aud (audience), exp (expiry Unix timestamp), iat (issued at), and jti (JWT ID for replay prevention).
HS256 vs RS256 β Choosing a Signing Algorithm
The alg field in the Header is not cosmetic β it determines the entire trust model of your system:
| Property | HS256 (HMAC-SHA256) | RS256 (RSA-SHA256) |
|---|---|---|
| Key Type | Single shared secret (symmetric) | Private key signs / Public key verifies (asymmetric) |
| Who can verify? | Anyone with the shared secret | Anyone with the public key (published at JWKS endpoint) |
| Who can sign? | Anyone with the shared secret | Only the Auth Server holding the private key |
| Use Case | Single-service apps where auth & API are the same trust boundary | Microservices, third-party APIs, multi-tenant SaaS |
| Security Risk | Shared secret leakage = full compromise; all services must hold it | Private key never leaves Auth Server; public key is safely shareable |
The industry standard for production microservices is RS256 (or ES256/EdDSA for smaller key sizes). Auth Servers publish their public keys at a /.well-known/jwks.json endpoint (JWKS β JSON Web Key Set), which any Resource Server can poll to verify tokens without contacting the Auth Server.
OAuth 2.1 Authorization Code Flow + PKCE
The Authorization Code Flow is the most secure OAuth 2.0 grant type for user-facing apps. PKCE (Proof Key for Code Exchange, RFC 7636) is now mandatory for all public clients (SPAs, mobile apps) in OAuth 2.1 because the old Implicit Flow was deprecated as fundamentally insecure.
| Grant Type | Use Case | PKCE? | Status |
|---|---|---|---|
| Authorization Code + PKCE | SPAs, mobile apps, web apps with user login | Required | Recommended |
| Client Credentials | M2M: CI/CD, backend services, daemons | N/A (no user) | Recommended |
| Implicit Flow | Former SPA pattern (token in URL fragment) | Not applicable | Deprecated (OAuth 2.1) |
| Resource Owner Password | Legacy first-party apps sending username/password | Not applicable | Deprecated (OAuth 2.1) |
Access Tokens vs Refresh Tokens β The Two-Token Strategy
The two-token strategy balances security (short-lived proof of access) with usability (no repeated logins):
| Property | Access Token (JWT) | Refresh Token |
|---|---|---|
| Lifetime | Short: 15 min to 1 hour | Long: 1 day to 90 days |
| Sent to | Resource Server API (every request) | Auth Server /token endpoint only |
| Storage | In-memory (JavaScript variable) | HttpOnly + SameSite=Strict cookie |
| Revocable? | No (stateless JWT; valid until exp) | Yes (Auth Server DB lookup) |
| Rotation | Issued fresh with each new Refresh Token exchange | Rotate on use (detect reuse = session hijack indicator) |
Refresh Token Rotation (RTR) is a critical security pattern: when a Refresh Token is used, it is immediately invalidated and replaced with a new one. If an old Refresh Token is ever presented (reuse detected), the Auth Server invalidates the entire token family β a strong indicator that the token has been stolen.
Real-World Applications
Social Login
"Login with Google / GitHub / Apple" β OAuth 2.0 Authorization Code Flow + PKCE delegates identity to a trusted provider. Your app never sees the user's Google password.
Microservices API Security
A microservice validates incoming requests by verifying the JWT's RS256 signature against the Auth Server's public key β completely stateless, no database round-trip needed.
Mobile Apps (Public Clients)
iOS and Android apps must use PKCE because they cannot safely store a client_secret. The dynamic code_challenge/code_verifier pair prevents authorization code interception attacks.
Single-Page Applications (SPAs)
React/Vue SPAs store Access Tokens in memory (not localStorage) and Refresh Tokens in HttpOnly, SameSite=Strict cookies to mitigate XSS and CSRF attacks respectively.
Machine-to-Machine (M2M)
CI/CD pipelines and backend services use the OAuth 2.0 Client Credentials flow β no user interaction. The service authenticates with its own client_id + client_secret to get a short-lived Access Token.
OpenID Connect (OIDC) for SSO
Enterprise Single Sign-On systems like Okta and Azure AD use OIDC to issue ID Tokens containing user profile claims, enabling seamless login across dozens of internal apps.
Advantages
- Stateless verification β JWT enables auth without a database lookup, scaling horizontally to millions of requests per second with no shared session state.
- Granular permissions via Scopes β OAuth 2.0 scopes let users grant minimal, specific access (read:email only) instead of all-or-nothing account control.
- No password sharing β Delegated authorization means users never hand their credentials to third-party apps, eliminating the password anti-pattern entirely.
- Instant Refresh Token revocation β The Auth Server can invalidate a Refresh Token at any time, cutting off access even before the Access Token expires.
- Industry standard (RFC 6749 + RFC 7519) β Supported by every major auth provider: Google, GitHub, Microsoft, Okta, and Auth0 out of the box.
- Cross-platform delegation β Works identically for web apps, SPAs, mobile apps, CLI tools, and machine-to-machine (M2M) service-to-service flows.
Disadvantages
- Access Tokens cannot be revoked before expiry β A stolen JWT remains valid until the exp claim; mitigate with short TTLs (15β60 min) and token rotation.
- Payload is readable by anyone β The Payload is only base64-encoded, NOT encrypted. Any party with the token can instantly decode and read all claims.
- Token size overhead β JWTs are significantly larger than opaque session tokens, adding latency on every request through increased HTTP header payload.
- Algorithm confusion attacks β Misconfigured servers can accept forged alg:none tokens; always explicitly allowlist and validate the algorithm field.
- Refresh Token theft equals silent account takeover β A stolen long-lived Refresh Token grants unlimited new Access Tokens until explicitly revoked server-side.
- PKCE adds client complexity β Public clients must correctly implement code_verifier/code_challenge generation and exchange, or remain vulnerable to interception attacks.
Quick Reference Cheat Sheet
| Concept | Definition | Key Rule |
|---|---|---|
| OAuth 2.0 | Authorization framework (RFC 6749) β delegates limited access without sharing passwords. | Use scopes to apply least-privilege: read:contacts not full account access. |
| JWT | Signed token format (RFC 7519) β Header.Payload.Signature. NOT encrypted by default. | Never store passwords or PII in Payload β it is only base64-encoded, readable by anyone. |
| Access Token | Short-lived JWT (15 min β 1 hr) sent as Bearer token in Authorization header. | Store in memory only (JS variable) β never in localStorage (XSS risk). |
| Refresh Token | Long-lived opaque token used to silently obtain new Access Tokens. | Store in HttpOnly, SameSite=Strict cookie only. Rotate on every use. |
| HS256 vs RS256 | HS256 = symmetric (shared secret). RS256 = asymmetric (private signs, public verifies). | Always use RS256 in microservices β private key never leaves the Auth Server. |
| PKCE | Proof Key for Code Exchange β prevents auth code interception in public clients. | Mandatory for all SPAs and mobile apps. Implicit Flow is fully deprecated. |
Frequently Asked Questions (FAQ)
Q.What is the difference between OAuth 2.0 and JWT?
Q.What is the difference between Authentication and Authorization?
Q.Can a JWT be decrypted by a hacker?
Q.Why are Refresh Tokens necessary?
Q.What is OpenID Connect (OIDC)?
Q.What is PKCE and why is it required for public clients?
Q.Where should Access Tokens and Refresh Tokens be stored in a browser?
Related Topics
Test Your Knowledge
Ready to prove your skills? Take our rigorous multiple-choice quiz designed to test your understanding of this topic and prepare you for interviews.