OAuth 2.0 PKCE Protection for JavaScript SPAs

OAuth 2.0 Authorization Code Flow with Proof Key for Code Exchange (PKCE) protects JavaScript Single-Page Applications (SPAs) by dynamically binding authorization requests to token exchanges using cryptography. Because browser-based applications run entirely on the client side, they cannot securely store a static client secret. PKCE solves this vulnerability by replacing static secrets with a dynamically generated, one-time cryptographic proof, preventing malicious actors from intercepting authorization codes and exchanging them for access tokens.

The Security Problem with SPAs

JavaScript single-page applications are classified as “public clients.” Any code, configuration file, or secret embedded within the application is entirely exposed to the user and can be inspected via browser developer tools.

Historically, SPAs utilized the OAuth 2.0 Implicit Flow, which returned access tokens directly in the URL hash fragment. This approach exposed tokens to browser history logs, referrer headers, and malicious browser extensions. While the standard Authorization Code Flow keeps tokens out of URLs, it traditionally requires a client_secret during the token exchange—something an SPA cannot safely protect.

How PKCE Works in an SPA

PKCE (defined in RFC 7636) modifies the standard Authorization Code Flow by introducing a dynamically generated secret for each authorization request. The process follows these steps:

  1. Generation of the Code Verifier: When a user initiates a login, the SPA generates a cryptographically random string called the code_verifier. This value is stored temporarily in secure browser memory or session storage.
  2. Creation of the Code Challenge: The SPA transforms the code_verifier using a SHA-256 cryptographic hash function and encodes it with Base64URL to create the code_challenge.
  3. Authorization Request: The SPA redirects the browser to the authorization server, appending the code_challenge and the method used (code_challenge_method=S256) to the query parameters.
  4. Code Issuance: The user authenticates with the authorization server. Upon approval, the server stores the code_challenge and redirects the user back to the SPA with an authorization_code.
  5. Token Exchange: The SPA sends a POST request directly to the authorization server’s token endpoint containing the received authorization_code and the original, unhashed code_verifier.
  6. Verification and Token Issuance: The authorization server hashes the received code_verifier using SHA-256 and compares it against the code_challenge stored in step 4. If the values match, the server verifies that the client requesting the tokens is the exact same client that initiated the login, and issues the access and refresh tokens.

Key Security Benefits of PKCE for SPAs