Authentication is one of the most error-prone parts of a frontend application. Get it wrong and you have account takeover, session hijacking, or worse. Senior frontend interviews probe whether you understand the modern patterns: cookies vs tokens, OAuth flows, refresh strategies, and the security implications of each choice.
The two main patterns
Session cookies
Server creates a session, stores in DB. Client gets a session ID in a cookie. Server validates the cookie on every request.
Pros:
- HttpOnly cookies are not readable from JS — XSS-safe for the token
- Centralized session control on server
- Easy logout (server invalidates session)
Cons:
- Requires same-origin or careful CORS setup
- Stateful server (database lookup per request) — though usually cached
JWT tokens
Server signs a JWT with claims about the user. Client sends it in Authorization header.
Pros:
- Stateless — no server-side session lookup
- Works across domains and microservices
- Standards-based
Cons:
- Cannot easily revoke (until expiry)
- Token storage on client is a security headache
- Larger payload than session cookies
Where to store tokens
This is the most-debated topic. The options:
- localStorage: JS-readable. XSS can steal tokens. Wrong choice.
- sessionStorage: same XSS risk; cleared on tab close.
- HttpOnly cookies: not JS-readable. XSS cannot steal directly. Best for tokens.
- In-memory: JS variable. Safe from XSS but lost on reload — paired with refresh tokens.
The refresh token pattern
Modern recommended pattern:
- Short-lived access token (5–15 minutes), in memory
- Long-lived refresh token (1–30 days) in HttpOnly Secure SameSite=Strict cookie
- Access token expires → use refresh token to get new access token
- Refresh token compromised → server can revoke
This bounds the damage of token theft and gives the server centralized control.
OAuth and OpenID Connect
For “Sign in with Google/Apple/GitHub” flows:
- Frontend redirects user to provider with client_id
- User authenticates with provider
- Provider redirects back with authorization code
- Frontend sends code to backend
- Backend exchanges code for access + ID tokens directly with provider
- Backend validates ID token, creates own session for the user
Critical: never expose client_secret in frontend. Token exchange must happen server-side.
PKCE (Proof Key for Code Exchange)
Modern OAuth flows use PKCE to prevent authorization code interception. The flow:
- Frontend generates a random code_verifier; hashes to code_challenge
- Sends code_challenge with the auth request
- Provider returns code; frontend sends code + code_verifier
- Provider validates: code_verifier hashed must equal code_challenge
Used in SPAs and mobile apps. Most OAuth libraries handle PKCE automatically.
Handling auth state in React
Common pattern:
- useAuth() hook exposes current user and login/logout
- Provider at app root subscribes to auth state changes
- ProtectedRoute redirects unauthenticated users
- useEffect on app mount checks for existing session (e.g., calls /me endpoint)
Logout
For session cookies: hit a logout endpoint; server clears the session and Set-Cookie expires the cookie.
For JWTs: clear in-memory token and refresh token. Optionally call a revocation endpoint to invalidate the refresh token server-side.
Multi-device: server-side revocation lists allow logout-from-all-devices.
Multi-factor authentication
Common UX:
- User enters username + password
- Server returns “MFA required” with a temporary token
- User enters TOTP code or approves push
- Server validates; returns access token
WebAuthn (passkeys) is becoming standard. Modern auth allows skipping passwords entirely with passkey + device verification.
Common mistakes
- Storing JWTs in localStorage
- Logging tokens (in error reports, console)
- Sending tokens via URL query parameters (logged in server access logs)
- Not using HTTPS (cleartext credentials)
- Not validating tokens server-side (trust)
- Long-lived access tokens (no compromise window mitigation)
Frequently Asked Questions
Should I roll my own auth or use a service?
Use a service. Auth0, Clerk, Supabase Auth, AWS Cognito, Firebase Auth — all are battle-tested. Rolling your own is risky; subtle bugs are catastrophic.
Should I use sessions or JWTs?
For monolithic web apps: sessions (HttpOnly cookies). For multi-service or mobile apps: JWTs with refresh tokens. Both are valid; sessions are simpler.
What about passkeys?
WebAuthn / passkeys are the future. They eliminate passwords. Adoption is growing — Apple, Google, Microsoft all support. Add as an option alongside passwords for now.