PKCE (Proof Key for Code Exchange) proves that the app that started the login is the same app that finishes it.
The steps in the browser:
- 1. Create secrets. Make a random
code_verifier(43 to 128 characters) and a randomstate. Computecode_challenge = BASE64URL(SHA-256(code_verifier)). Save the verifier and the state insessionStorage, so they survive the redirect. - 2. Redirect to login. Send the user to the authorization server's
/authorizeURL withresponse_type=code,client_id,redirect_uri,scope,state,code_challengeandcode_challenge_method=S256. - 3. Come back. After login, the server redirects to
redirect_uri?code=...&state=.... - 4. Check
state. If it doesn't match the saved value, stop. A mismatch means someone else started this login. - 5. Swap the code.
POSTto the/tokenendpoint withgrant_type=authorization_code, thecode,redirect_uri,client_idand thecode_verifier. There is no client secret, because an SPA can't hide one. - 6. Clean up. Delete the verifier and state. Remove
codefrom the address bar withhistory.replaceState.
What PKCE protects: a stolen authorization code is useless without the verifier, and the verifier never left the browser.
The older implicit flow put the token straight into the URL. It is no longer recommended. Use a well-tested library instead of writing this by hand.
- The SPA creates a random
code_verifierand sends only its SHA-256 hash as thecode_challenge. - It saves the verifier and
stateinsessionStorageso they survive the redirect. - It checks
statewhen the user comes back, and stops if it doesn't match. - It swaps the code plus the verifier for tokens, with no client secret.
- A stolen code is useless without the verifier.