oauth2-pkce-spa-security.md
OAuth2 + PKCE: Auth for Modern Single-Page Apps
Why implicit flow died, how PKCE protects public clients, and the token flow every React app should implement.
- Security
- OAuth
- Web
SPAs can't keep secrets your client ID lives in bundled JavaScript. OAuth2's implicit flow treated that as acceptable. It wasn't. PKCE fixed public client auth without a backend holding credentials.
The actors
- Resource owner the user
- Client your SPA or mobile app
- Authorization server issues tokens (Auth0, Keycloak)
- Resource server your API
Authorization code + PKCE flow
- Generate random
code_verifier(43โ128 chars) - Hash it โ
code_challenge(S256) - Redirect user to auth server with challenge
- User logs in, auth server returns authorization code
- Exchange code + original verifier for tokens
// Step 1 before redirect
const verifier = generateRandomString(64);
const challenge = base64url(sha256(verifier));
sessionStorage.setItem("pkce_verifier", verifier);
// Step 5 token exchange
const res = await fetch("/oauth/token", {
method: "POST",
body: new URLSearchParams({
grant_type: "authorization_code",
code,
code_verifier: sessionStorage.getItem("pkce_verifier"),
client_id: CLIENT_ID,
redirect_uri: REDIRECT_URI,
}),
});An attacker intercepting the code can't exchange it without the verifier they never saw.
Token storage
| Storage | XSS risk | Refresh |
|---|---|---|
| localStorage | High | Easy |
| memory | Lower | Lost on refresh |
| httpOnly cookie | Lowest | Needs BFF pattern |
For SPAs, Backend-for-Frontend (BFF) holding refresh tokens in httpOnly cookies is the production-grade pattern.
What to avoid
Implicit flowdeprecated (RFC 9700)- Long-lived access tokens in localStorage
- Client secrets in frontend bundles
[!WARNING] OAuth2 is a framework, not a plug-and-play library. Misconfigured redirect URIs and scopes cause more breaches than crypto breaks.
Takeaway
PKCE turns "public client" from a vulnerability into a defined threat model. Pair it with short-lived access tokens and secure refresh handling your users (and compliance team) will thank you.
Related
Continue reading
More notes on similar topics.
The horse is doing the running. You're still the one who has to stay balanced, read the terrain, and not fall off. That's the whole difference between vibe coding and actually directing the thing.
- AI Engineering
- Vibe Coding
The Day the Facility Guy Shipped a Feature to My App
July 29, 2026
I taught our building's maintenance guy how to vibe code for fun. Twenty minutes later he'd chatted his way into a working feature inside one of my real apps. Here's what actually happened, and why it didn't disprove anything I've written in this series.
- AI Engineering
- Vibe Coding
A five-level, self-scoring checklist to find out honestly whether your AI-assisted workflow is AI-Orchestrated Engineering or vibe coding with extra steps.
- AI Engineering
- Engineering Philosophy