Ask anything about this article
Hi! I've read this article.
What would you like to know?
@farhan

Developers are still wiring Single Page Applications (SPAs) to OAuth 2.0 using the plain Authorization Code flow. On paper it looks correct, but in practice it leaves a gaping hole that attackers are exploiting at scale. The root cause? Ignoring Proof Key for Code Exchange (PKCE). This hot take dives into why the PKCE omission is still common, what the real‑world impact looks like, and how the community can finally close the gap.
"If you think the classic Authorization Code flow is safe for SPAs, you are living in 2015."
Even though the OAuth 2.1 draft makes PKCE mandatory for public clients, many libraries and tutorials still showcase the older flow. The reasons are mostly cultural and practical:
oauth2-client-js without PKCE.The result is a silent epidemic: thousands of production SPAs exposing their authorization codes to malicious JavaScript or compromised browsers.
When an SPA uses the plain Authorization Code flow, the authorization code is delivered to the browser via a redirect URI. An attacker who can inject JavaScript (through XSS, malicious extensions, or compromised third‑party scripts) can read that code and exchange it for an access token—without ever needing the client secret (which SPAs never have). The steps are trivial:
code=XYZ in the URL.window.location.search and extracts XYZ.XYZ to the attacker’s server.XYZ at the token endpoint, obtaining access_token and optionally id_token.Because the code is single‑use, the attack works only once per victim, but that is enough to steal user data, perform actions on their behalf, or pivot to other services.
PKCE was introduced precisely to bind the authorization code to a code verifier that only the legitimate client knows. The flow adds two parameters:
code_challenge (sent in the authorization request, derived from a random verifier)code_verifier (sent in the token request)If an attacker intercepts the code but does not possess the original verifier, the token endpoint rejects the exchange. This effectively neutralizes the code‑interception vector.
| Feature | Auth Code Flow without PKCE | Auth Code Flow with PKCE |
|---|---|---|
| Client type | Public (SPA) | Public (SPA) |
| Secret required | No (but still vulnerable) | No |
| Code interception risk | High | Negligible |
| Compatibility with OAuth 2.1 | Deprecated | Required |
| Implementation effort | Minimal | Minimal (few extra lines) |
These incidents illustrate that the threat is not theoretical; it is actively being weaponized.
response_type=code in your front‑end code and verify that code_challenge is present.@azure/msal-browser, oidc-client-ts).code value – a sign of replay attacks.The OAuth 2.1 draft, now in final form, mandates PKCE for all public clients and deprecates the implicit flow entirely. This is part of a broader Zero Trust shift where every request must be verified, not just the initial login. Ignoring PKCE is equivalent to leaving a back door open in a Zero Trust world.
Adopting PKCE also aligns with other emerging best practices:
Developers love the "quick‑and‑dirty" approach, but the hidden cost is massive. A single compromised SPA can expose thousands of user accounts, trigger GDPR fines, and damage brand reputation. The ROI of adding PKCE is essentially zero – a few extra characters in the URL and a handful of lines of code – while the downside of not doing it is a potential data breach.
If you are still debating whether PKCE is worth the effort, ask yourself: Would you ship a car without seat belts because they add a few extra steps to the assembly line? The answer is obvious.
Bottom line: If your SPA does not use PKCE, it is not secure. Fix it today before the next breach makes headlines.