OAuth 2.0, OpenID Connect, and PKCE for Frontend Auth Flows

The frontend participates in OAuth/OIDC, but it should not become the trust anchor. The browser starts redirects, stores short-lived client state, receives callback parameters, and calls APIs or a Backend for Frontend. The authorization server authenticates the user and issues grants/tokens. Resource servers validate access tokens for the intended audience. Strong architecture makes these responsibilities explicit and keeps raw credentials out of places where cross-site scripting or third-party scripts can steal them.
Quick Navigation: Protocol Boundaries: OAuth, OpenID Connect, PKCE • Authorization Code with PKCE Flow • Tokens: ID Token, Access Token, Refresh Token • Browser Threat Model and Storage Choices • Backend for Frontend vs Pure SPA • Failure Modes and Recovery Policy • Senior Interview Answer Pattern
Quick Decision Guide
Senior-level answer: For browser apps, use Authorization Code with PKCE. OAuth 2.0 grants access to resources; OpenID Connect adds identity through an ID token and discovery metadata; PKCE binds the authorization request to the token exchange so an intercepted authorization code is not enough.
Browser apps are public clients: they cannot keep client secrets. That means the important design choices are redirect validation, state, nonce, PKCE, token lifetime, refresh strategy, storage threat model, and whether a Backend for Frontend should keep provider tokens server-side.
Do not describe OAuth as simply "login." Strong interview answers separate authorization, identity, browser constraints, token handling, and operational failure modes.
Protocol Boundaries: OAuth, OpenID Connect, PKCE
| Term | Full form | What it solves | What it does not solve |
|---|---|---|---|
| OAuth 2.0 | Open Authorization 2.0 | delegated access to resources | user identity by itself |
| OpenID Connect | identity layer on OAuth 2.0 | authentication claims and ID token validation | API authorization policy by itself |
| PKCE | Proof Key for Code Exchange | protects authorization code exchange for public clients | CSRF, issuer mix-up, token storage, or XSS by itself |
OAuth answers: can this client get access to this resource?
OpenID Connect answers: which user authenticated at this issuer?
PKCE answers: is the party redeeming this code the one that started the flow?A strong frontend answer keeps those questions separate. If you blur them, you end up using ID tokens as API access tokens, skipping nonce validation, or assuming PKCE makes every browser-storage choice safe.
Authorization Code with PKCE Flow
A browser app cannot safely store a client secret, so it uses Authorization Code with PKCE.
Browser app
-> generate code_verifier
-> derive code_challenge
-> redirect to authorization server with state, nonce, code_challenge
Authorization server
-> authenticate user
-> redirect back with authorization code
Browser or Backend for Frontend
-> exchange code + code_verifier
-> receive tokens or create application session| Value | Purpose |
|---|---|
code_verifier | secret generated by the client for this flow |
code_challenge | derived value sent in the authorization request |
state | correlates response to request and helps defend redirect CSRF |
nonce | binds the ID token to this authentication request |
| exact redirect URI | prevents codes being sent to attacker-controlled callbacks |
Tokens: ID Token, Access Token, Refresh Token
| Token | Used by | Purpose | Common frontend mistake |
|---|---|---|---|
| ID token | client application | prove authentication facts about the user | using it to authorize API calls |
| access token | resource server/API | authorize access to protected resources | storing long-lived bearer tokens in browser storage |
| refresh token | client or secure server-side component | obtain new access tokens | allowing uncontrolled silent refresh loops |
Browser Threat Model and Storage Choices
| Pattern | Benefit | Cost |
|---|---|---|
| in-memory token | lower persistence after reload | refresh and tab coordination become harder |
| session cookie via Backend for Frontend | hides provider tokens from JavaScript | requires CSRF-aware cookie/session design |
| localStorage/sessionStorage | simple persistence | larger XSS blast radius for bearer tokens |
Backend for Frontend vs Pure SPA
A pure Single Page Application can run Authorization Code with PKCE, but it still has to handle token exposure, refresh, logout, and cross-tab coordination in JavaScript.
A Backend for Frontend (BFF) changes the boundary:
Browser -> BFF session cookie -> BFF holds provider tokens -> APIsBFF strengths
BFF costs
The staff-level answer is not "always use BFF." It is: use BFF when token exposure, enterprise controls, auditability, or refresh reliability justify the operational cost.
Failure Modes and Recovery Policy
| Failure mode | Symptom | Better policy |
|---|---|---|
| callback URI mismatch | user stuck in redirect error | exact environment-specific redirect configuration |
| missing state validation | login CSRF or response confusion | generate, store, and verify state per request |
| missing nonce validation | ID token replay/confusion risk | validate nonce for OpenID Connect flows |
| refresh race across tabs | repeated 401s or refresh storm | single-flight refresh and broadcast auth state |
| provider outage | login and refresh fail | bounded retries, clear re-auth UX, incident messaging |
| token accepted by wrong API | confused resource access | validate audience and issuer at resource servers |
Senior Interview Answer Pattern
| Prompt | Strong response |
|---|---|
| Does PKCE replace state? | No. PKCE protects code exchange; state correlates the redirect response and helps defend redirect CSRF. |
| Is OAuth authentication? | OAuth is authorization. OpenID Connect adds authentication semantics. |
| Can a SPA keep a client secret? | No. Browser apps are public clients. |
| Is localStorage always forbidden? | The better answer is threat-model based, but bearer tokens in JavaScript-accessible storage increase XSS blast radius. |