OAuth 2.0 Security Best Practices
OAuth 2.0 is the standard for delegated authorization, but the spec is flexible enough to be implemented insecurely. This guide covers what goes wrong in practice and how to avoid it.
Always Use PKCE
Proof Key for Code Exchange (PKCE) prevents authorization code interception attacks. Originally designed for mobile apps, it is now recommended for all OAuth clients, including server-side web apps.
import crypto from "crypto";
// Generate code verifier (random string)
const codeVerifier = crypto.randomBytes(32).toString("base64url");
// Generate code challenge (SHA-256 hash of verifier)
const codeChallenge = crypto
.createHash("sha256")
.update(codeVerifier)
.digest("base64url");
// Send code_challenge in authorization request
// Send code_verifier in token exchange
Reference: RFC 7636 — PKCE
The current best practice document from the IETF recommends PKCE for all clients and requires authorization servers to support it:
Reference: OAuth 2.0 Security Best Current Practice (RFC 9700)
Validate Redirect URIs Strictly
Open redirect vulnerabilities in OAuth are devastating — they allow token theft.
- Register exact redirect URIs. No wildcards.
- Match the full URI including path. Do not match on domain alone.
- Never allow user-supplied redirect URIs without strict allowlist validation.
Bad:
redirect_uri=https://example.com/callback ✓
redirect_uri=https://example.com.evil.com ✗ (but may pass loose matching)
redirect_uri=https://example.com/callback/../evil ✗ (path traversal)
Reference: OAuth 2.0 — Redirect URI Validation (RFC 6749 §3.1.2)
Use the Authorization Code Flow
For server-side apps, always use the Authorization Code flow (with PKCE). Never use the Implicit flow — it exposes tokens in the URL fragment and has been deprecated.
Reference: OAuth 2.0 Security BCP — Implicit Flow (RFC 9700)
Validate the state Parameter
The state parameter prevents CSRF attacks on the OAuth flow:
- Generate a random, unguessable
statevalue. - Store it in the user's session before redirecting to the authorization server.
- Verify it matches when the user returns to your callback.
import crypto from "crypto";
// Before redirect
const state = crypto.randomBytes(16).toString("hex");
session.oauthState = state;
// In callback
if (req.query.state !== session.oauthState) {
throw new Error("Invalid state — possible CSRF");
}
delete session.oauthState;
Token Storage
- Access tokens: short-lived (minutes), stored in memory or HttpOnly cookies.
- Refresh tokens: stored server-side (database, encrypted cookie). Never in localStorage.
- ID tokens (OIDC): validate signature and claims, then extract user info. Do not use as access tokens.
Validate ID Tokens Properly (OIDC)
If using OpenID Connect, validate the ID token:
- Verify the signature against the provider's JWKS endpoint.
- Check
issmatches the provider. - Check
audmatches your client ID. - Check
exphas not passed. - Check
noncematches what you sent (prevents replay).
Reference: OpenID Connect Core — ID Token Validation
Scope Minimization
Request only the scopes you need. Broad scopes increase the blast radius if a token is compromised.
// BAD
scope=openid profile email admin read write
// GOOD
scope=openid email
Token Lifetime and Rotation
- Access tokens: 5–15 minutes.
- Refresh tokens: rotate on every use (issue a new refresh token with each access token refresh).
- Detect refresh token reuse — if a refresh token is used twice, revoke the entire token family.
Reference: OAuth 2.0 Security BCP — Refresh Token Protection (RFC 9700)
Prevent Token Leakage
- Do not pass tokens in URL query parameters (they end up in logs and referrer headers).
- Use the
Authorization: Bearerheader for API calls. - Set
Referrer-Policy: strict-origin-when-cross-originto prevent token leakage via referrer. - Do not log tokens.
Provider-Specific Hardening
- Verify tokens using Google's tokeninfo endpoint or JWKS: Google — Verify ID Tokens
- Use
hdparameter to restrict to specific Google Workspace domains.
Microsoft / Entra ID
- Validate
tid(tenant ID) to prevent token from other tenants: Microsoft — Token Validation - Use v2.0 endpoints, not v1.0.
GitHub
- Always verify the
stateparameter on callback. - Limit OAuth app scopes: GitHub — OAuth Scopes
Common Mistakes
- Not using PKCE (even on backend apps).
- Loose redirect URI matching.
- Using Implicit flow.
- Storing tokens in localStorage.
- Not validating
stateon callback. - Requesting excessive scopes.
- Using ID tokens as access tokens.
Related Guides
Content is AI-assisted and reviewed by our team, but issues may be missed and best practices evolve rapidly, send corrections to [email protected]. Always consult official documentation and validate key implementation decisions before making design or security choices.
