OAuth2 Authentication Architecture: Grant Types, JWT Tokens & SSO
OAuth2 authentication is the backbone of modern authorization on the web. Every time you click "Sign in with Google" or grant a third-party app access to your calendar, OAuth2 is doing the heavy lifting behind the scenes.
This guide covers the full OAuth2 authentication architecture — grant types, token mechanics, SSO with OpenID Connect, secure storage patterns, and how it all compares to API keys and session-based auth.
OAuth2 Grant Types#
OAuth2 defines several flows (grant types), each designed for a different context.
Authorization Code Flow#
The most common OAuth2 flow for server-side web apps. The client redirects the user to the authorization server, which returns an authorization code. The server exchanges that code for tokens.
User → App → Auth Server (login + consent)
Auth Server → App (authorization code)
App Server → Auth Server (code + client_secret → tokens)
This keeps the client_secret on the server, never exposed to the browser.
Authorization Code with PKCE#
For single-page apps and mobile apps where you cannot store a client_secret safely. PKCE (Proof Key for Code Exchange) adds a dynamically generated code_verifier and code_challenge to prevent authorization code interception.
// Generate PKCE challenge
const codeVerifier = generateRandomString(64);
const codeChallenge = base64url(sha256(codeVerifier));
// Authorization request includes code_challenge
const authUrl = `https://auth.example.com/authorize?
response_type=code&
client_id=YOUR_CLIENT_ID&
redirect_uri=https://app.example.com/callback&
code_challenge=${codeChallenge}&
code_challenge_method=S256&
scope=openid profile email`;
Client Credentials Flow#
Machine-to-machine communication where no user is involved. The client authenticates directly with its own credentials.
const response = await fetch("https://auth.example.com/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: SERVICE_CLIENT_ID,
client_secret: SERVICE_CLIENT_SECRET,
scope: "read:metrics write:logs",
}),
});
Device Authorization Flow#
For devices with limited input (smart TVs, CLI tools). The device displays a code, and the user authorizes on a separate device with a browser.
Device → Auth Server: request device code
Device → User: "Go to https://auth.example.com/device and enter code: ABCD-1234"
Device polls Auth Server until user completes authorization
Token Types#
OAuth2 issues several token types, each with a distinct role.
| Token | Purpose | Lifetime |
|---|---|---|
| Access token | Authorizes API requests | Minutes (5–60) |
| Refresh token | Obtains new access tokens | Days to months |
| ID token (OIDC) | Carries user identity claims | Minutes |
JWT Structure#
Most OAuth2 implementations use JWTs (JSON Web Tokens) for access and ID tokens. A JWT has three base64url-encoded parts separated by dots:
header.payload.signature
// Header
{ "alg": "RS256", "typ": "JWT", "kid": "key-2026-03" }
// Payload
{
"sub": "user_8291",
"iss": "https://auth.example.com",
"aud": "https://api.example.com",
"exp": 1743206400,
"iat": 1743202800,
"scope": "read:profile write:settings",
"email": "user@example.com"
}
// Signature
RSASHA256(base64url(header) + "." + base64url(payload), privateKey)
Always validate iss, aud, exp, and the signature on every request. Never trust claims without verification.
Token Storage: httpOnly Cookies vs localStorage#
Where you store tokens in the browser has major security implications.
| Approach | XSS Risk | CSRF Risk | Recommendation |
|---|---|---|---|
localStorage | High — JS can read tokens | None | Avoid for sensitive tokens |
httpOnly cookie | Protected — JS cannot access | Moderate | Preferred, with CSRF protection |
| In-memory (JS variable) | Moderate — cleared on refresh | None | Good for short-lived SPAs |
Best practice: Store refresh tokens in httpOnly, Secure, SameSite=Strict cookies. Keep access tokens in memory. Use a silent refresh or rotating refresh tokens to maintain sessions.
// Server sets token cookie
res.cookie("refresh_token", refreshToken, {
httpOnly: true,
secure: true,
sameSite: "strict",
path: "/api/auth/refresh",
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
});
SSO with OpenID Connect (OIDC)#
OpenID Connect is an identity layer on top of OAuth2. It adds the id_token and a standard /userinfo endpoint, making SSO implementation straightforward.
App A → Auth Server: authorize (scope: openid profile)
User logs in once
Auth Server → App A: id_token + access_token
...
App B → Auth Server: authorize (scope: openid email)
Auth Server recognizes session → skips login
Auth Server → App B: id_token + access_token
Key OIDC concepts:
- Discovery document at
/.well-known/openid-configurationpublishes endpoints and supported features. - ID token contains identity claims (
sub,email,name) — verified by signature, not by calling an API. - Scopes like
openid,profile,emailcontrol which claims appear in the token.
OAuth2 vs API Keys vs Session Auth#
| Factor | OAuth2 | API Keys | Session Auth |
|---|---|---|---|
| Delegation | Users grant scoped access to third parties | Full access, no delegation | No delegation |
| Granularity | Fine-grained scopes | All-or-nothing | Tied to user session |
| Revocation | Per-token, per-app | Rotate entire key | Destroy session |
| Best for | Third-party integrations, SSO | Server-to-server, internal tools | Traditional server-rendered apps |
Use OAuth2 when you need delegated, scoped access. Use API keys for simple internal service communication. Use session auth for monolithic apps with server-rendered pages.
Security Best Practices#
- Always use PKCE — even for confidential clients, PKCE adds defense in depth.
- Short-lived access tokens — 5 to 15 minutes. Use refresh tokens to renew.
- Rotate refresh tokens — issue a new refresh token with each use and invalidate the old one.
- Validate the
stateparameter — prevents CSRF on the authorization callback. - Restrict redirect URIs — register exact URIs, never use wildcards.
- Use
RS256overHS256— asymmetric signing lets resource servers verify tokens without sharing a secret. - Implement token revocation — provide an endpoint and honor revoked tokens promptly.
Common Mistakes#
- Storing tokens in
localStoragewithout understanding XSS exposure. - Not validating JWT claims — trusting the token without checking
iss,aud, andexp. - Using the implicit flow — deprecated in OAuth 2.1. Use authorization code with PKCE instead.
- Long-lived access tokens — if an access token lives for days, a leak becomes catastrophic.
- Ignoring token size — JWTs in headers can exceed proxy or CDN limits. Keep claims lean.
- Skipping PKCE for public clients — leaves the authorization code vulnerable to interception.
When to Choose What#
Need third-party access? → OAuth2 (authorization code + PKCE)
Machine-to-machine? → OAuth2 (client credentials) or mutual TLS
Internal service, simple auth? → API key with IP allowlist
Server-rendered monolith? → Session cookies
Need SSO across apps? → OAuth2 + OpenID Connect
OAuth2 authentication architecture is not just about login buttons. It is a framework for secure, scoped, revocable delegation — and understanding its moving parts is what separates a working integration from a secure one.
Build smarter systems at codelit.io.
Explore more system design guides at codelit.io/blog.
Try these templates
Continue learning
Go deeper on security
As an Amazon Associate I earn from qualifying purchases. Codelit may receive a commission at no extra cost to you.
Real-World Cryptography
David Wong · 2021
Which primitive to pick for TLS, JWTs or end-to-end messaging, and what breaks when you pick wrong.
4.7 (100)#14 in Client-Server Networking SystemsSecurity Engineering
Ross Anderson · 2020
Failure case studies across banking, hardware and protocols, plus the economics of why attacks pay.
4.8 (274)Kindle editionBuilding Secure and Reliable Systems
Heather Adkins, Betsy Beyer, et al. · 2020
Google's design reviews, incident write-ups and rollback practices for security at production scale.
4.6 (164)Release It!
Michael T. Nygard · 2018
Post-mortems of real outages, then the stability patterns (circuit breaker, bulkhead) that stop them.
4.6 (501)#4 in Client-Server Networking Systems