OAuth Mistakes in Production
This post is a checklist disguised as war stories
Every item below appears in real audits. OAuth did not fail — configuration and application code did.
1. Redirect URI laxity
Mistake: Register https://app.example.com/* or use open redirectors.
Why it hurts: Authorization codes go to attacker-controlled pages.
Fix:
Exact match only (scheme, host, port, path)
Separate clients for dev/stage/prod
No wildcard redirect URIs in production
2. Missing or weak state
Mistake: Omit state or use a static value.
Why it hurts: Login CSRF — victim links attacker's account to victim's session.
Fix: Cryptographically random state, bound to user agent session, verified on callback.
3. PKCE not enforced
Mistake: Public client without PKCE; or code_challenge_method=plain.
Why it hurts: Stolen authorization codes become tokens.
Fix: S256 PKCE for all clients; OAuth 2.1 makes this mandatory.
4. Tokens in the wrong place
| Token | Never | Prefer |
|---|---|---|
| Refresh token | localStorage, mobile logs |
Server-side store, httpOnly cookie |
| Access token | URL query strings | Authorization header |
| ID token | Sent to third-party APIs | Client login only |
Mistake: SPA stores refresh token in localStorage "because it's easier."
Why it hurts: Any XSS exfiltrates long-lived credentials.
5. Trusting the ID token at the API layer
Mistake: Resource server accepts id_token as bearer token.
Why it hurts: ID token is for the client, not your orders API. Wrong audience.
Fix: APIs require access tokens with correct aud (or introspection).
6. JWT validation shortcuts
Mistake: Decode JWT, read claims, skip signature verification "in dev."
Why it hurts: alg: none and key confusion attacks.
Fix: Always verify:
Signature (JWKS from
iss)exp/nbfissmatches expected realmaudcontains your API identifierScope for the operation
// Pseudocode — never hand-roll in production without a library
jwt.verifySignature(jwksUrl)
.requireIssuer("https://auth.example.com/realms/myrealm")
.requireAudience("orders-api");
7. Confusing authentication with authorization
Mistake: "User has a valid token" → full admin access.
Fix: Map IdP identity to your roles. Check permissions per resource.
8. One realm to rule them all (badly)
Mistake: Dev and prod share a realm; test users in production; admin console on public internet without IP restrict.
Fix: Environment isolation, least-privilege admin, MFA on IdP admin accounts.
9. Ignoring logout and session drift
Mistake: Clear only your app cookie; IdP SSO cookie remains.
Fix: OIDC end-session endpoint; short access token TTL; refresh rotation.
10. Logging secrets
Mistake: Log full token responses, client_secret, code_verifier.
Fix: Structured logs with token prefix only; never log secrets.
Production launch checklist
Authorization server (Keycloak / Okta / Auth0)
[ ] Production realm / tenant separate from dev
[ ] Redirect URIs exact match per environment
[ ] Client auth method appropriate (secret for confidential, PKCE for public)
[ ] Scopes are minimal (no
offline_accessunless needed)[ ] MFA policy defined
[ ] Admin access restricted and audited
[ ] TLS everywhere; HSTS on auth domain
Application
[ ]
statevalidated on every callback[ ] PKCE S256 on all authorization requests
[ ] Refresh tokens only server-side (or BFF pattern)
[ ] Session cookie:
Secure,HttpOnly,SameSite=LaxorStrict[ ] CSRF protection on state-changing endpoints
[ ] Error pages do not leak codes or tokens in HTML
APIs
[ ] Access token validation with full claim checks
[ ] Authorization separate from token presence
[ ] Rate limiting and abuse detection
[ ] Clock skew tolerance configured (~60s)
Operations
[ ] JWKS caching with key rotation support
[ ] Runbook: IdP outage, key rollover, mass revocation
[ ] Alerts on spike in
invalid_grant,invalid_client
Go deeper
Token binding and DPoP
For high-assurance APIs, consider Demonstrating Proof-of-Possession (DPoP) — binds tokens to a client key pair. Supported in some stacks; not universal yet.
Security headers for SPAs
Content-Security-Policy, frame-ancestors, no inline scripts — reduces XSS risk that makes token theft possible.
Penetration test focus areas
Ask testers to target: redirect manipulation, state bypass, ID token misuse, refresh token replay, and scope escalation.
What you should know after this post
[ ] You have a launch checklist copied into your project wiki
[ ] You know the top three browser-side mistakes (redirect,
state, storage)[ ] You validate JWTs completely, not cosmetically