Oauth2 & Oidc
For backend engineers implementing OAuth 2.0 and OpenID Connect in server-side web applications. Covers the authorization code flow with PKCE. Assumes familiarity with OAuth roles (client, authorization server, resource server).
Authorization URL
Use PKCE (Proof Key for Code Exchange) to prevent authorization code injection, and state to prevent CSRF. Both are mandatory for security.
Callback Handler
Swaps the code for tokens and validates the ID Token signature/claims.
Session Management
Use HttpOnly cookies to protect against XSS and enforce HTTPS only. Session should depend on an interface so the concrete implementation is swappable (e.g., in-memory, Redis). Session TTL: should match the Refresh Token TTL (or a fixed policy like “24 hours of inactivity”).
Token Management
Access Token TTL
Short (e.g., 5–15 minutes).
Refresh Token Failure
Check if refresh attempt fails (e.g., the refresh token is expired). Handle failure gracefully: delete the session, force re-login (same as logout mechanism).
Proactive Refresh
Don’t wait for the token to actually expire and fail a request. Instead, check the token’s age on every request and refresh it before it dies. Refresh Token TTL: Long (e.g., 7–30 days).
Concurrency Race
If a user loads a dashboard that fires 5 API requests simultaneously (e.g., fetching profile, graph data, notifications), and their token is expired:
- Request A sees expired token → starts refreshing.
- Request B sees expired token → starts refreshing.
- Request A gets a new Refresh Token (RT-2).
- Request B tries to use the old Refresh Token (RT-1).
- The OIDC provider sees RT-1 being used twice. It assumes theft and revokes everything. The user is logged out.
How to fix this?
- Simple fix: Use a large leeway (e.g., 5 minutes). It is statistically unlikely that a user makes requests exactly at the moment of expiry if you refresh 5 minutes early.
- Robust fix (locking): If you use Redis, set a temporary
refresh_in_progresslock. If Request B sees the lock, it waits 100ms and reads the session again (which will have the new token from Request A).
Middleware
For protected routes, require the user to be logged in first.
Logout
Delete the local session (destroy the session in Redis/Memory and clear the cookie).