React authentication with PKCE
In short
A React single-page app should use the Authorization Code flow with PKCE, because it cannot keep a client secret — anything in the bundle is public.
- No client secret. Ever. It ships to every visitor.
- PKCE replaces the secret with a per-request verifier and challenge.
- Verify the state parameter on the callback route before doing anything else.
The flow: Authorization Code with PKCE
- 1
Generate the verifier and challenge
Create a random verifier, hash it with SHA-256, base64url-encode it as the challenge, and keep the verifier in session storage until the callback.
Full detail in the docs - 2
Redirect to authorize
Send response_type=code with your client ID, redirect URI, scopes, state, the challenge and code_challenge_method=S256.
- 3
Handle the callback route
Compare the returned state against the one you stored, then exchange the code and verifier for tokens.
Full detail in the docs - 4
Store tokens deliberately
Decide where tokens live and why. Anything in localStorage is readable by any script that gets onto the page.
Common mistakes
- Using the implicit flow. It is deprecated and returns tokens in the URL fragment.
- Leaving the verifier in storage after a failed exchange.
- Registering a wildcard redirect URI in development and shipping it to production.
SPA with PKCE
Open the guide