## TL;DR
PKCE protects the authorization code flow on clients that cant keep a secret (SPAs, mobile apps) by having the app create a random code_verifier, send its SHA-256 hash as code_challenge with the login request, then present the original verifier when swapping the code for tokens. The server only issues tokens if the verifier matches the challenge, so a stolen authorization code is useless. Always use the S256 method, never plain.

```text
how to implement PKCE for OAuth public clients
```

## Use this when
- You are implementing login in a single-page app with no backend
- You are building a native mobile app that talks to an OAuth provider
- Your identity provider docs say PKCE is required (most now do)
- You are reviewing an OAuth integration and want to check it follows current best practice

## Not for
- Server-side web apps (confidential clients): use a client secret plus PKCE as defense in depth, but the secret is the main credential there
- Machine-to-machine client-credentials flows (no user, no PKCE)
- Explaining OAuth scopes or consent screens

## Steps
1. Generate a code_verifier: a cryptographically random string, 43 to 128 characters, using unreserved characters (letters, digits, hyphen, period, underscore, tilde). Use your platforms secure random generator, never Math.random.
   Expected output: a 64+ character random string that is different on every login attempt.

2. Derive the code_challenge with S256: SHA-256 hash the verifier, then base64url-encode the hash with no padding. This is the value that travels in the authorization URL; the verifier itself never leaves the app until the token exchange.
   Expected output: a base64url string (no plus, slash, or equals characters) derived from the verifier.

3. Start the authorization request with challenge and state. Redirect the user to the authorization endpoint with response_type=code, your client_id, redirect_uri, scope, a random state value, code_challenge=[challenge], and code_challenge_method=S256. Store the verifier and state in memory (or session storage) for this login attempt.
   Expected output: the provider shows the login/consent screen, and the URL carries your challenge and method.

4. Validate state on the callback, then exchange the code. When the provider redirects back with a code and state, first check the state matches what you stored (this blocks CSRF). Then POST to the token endpoint with grant_type=authorization_code, the code, redirect_uri, client_id, and code_verifier=[verifier].
   Expected output: the token endpoint returns an access token (and usually a refresh token); a wrong or missing verifier returns an error instead.

5. Never send a client secret from a public client. If your old code has one baked into the app bundle, remove it; anything shipped to a browser or phone is public. Rotate the secret if one was ever exposed this way.
   Expected output: the app bundle and repo contain no client secret for this OAuth client.

6. Treat tokens carefully after issuance. Keep the access token in memory, use short lifetimes, and send refresh tokens only over HTTPS with secure storage (see the companion skill on storing tokens in SPAs).
   Expected output: tokens live in memory or secure storage, never in URLs or permanent logs.

## Variant phrasings
- "OAuth PKCE flow step by step for React app"
- "code_verifier code_challenge example S256"
- "do I need PKCE for mobile app OAuth"
- "PKCE vs client secret for public clients"

## Edge cases and pitfalls
- Some providers still accept code_challenge_method=plain, which defeats the purpose if an attacker sees the challenge. Enforce S256 on the provider side if it lets you.
- The verifier must be single-use and tied to one login attempt; reusing it across attempts weakens the binding.
- PKCE does not replace the state parameter; you need both (PKCE stops code interception, state stops CSRF).

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_-_MxetK8ScOArvSpEJq7FQ
