What this tool does
PKCE (Proof Key for Code Exchange, RFC 7636) stops a stolen authorization code from being redeemed by anyone except the client that asked for it. Before redirecting the user, the client creates a random code_verifier of 43 to 128 characters from A–Z, a–z, 0–9 and - . _ ~ and sends only its code_challenge, BASE64URL(SHA-256(code_verifier)) without padding, in the authorization request, with code_challenge_method=S256. When it exchanges the code at the token endpoint it sends the verifier itself; the server hashes it and compares the result with the challenge it stored. The generator follows the RFC’s recipe: 32 random bytes from crypto.getRandomValues, base64url-encoded to 43 characters, with longer options up to 96 bytes (128 characters). A verifier you paste is checked against the RFC’s length and character rules. Check a pair recomputes the challenge and compares it exactly, as a server does, and names the usual mistakes: standard Base64 with + / and =, a hex-encoded hash, or the plain method by accident.
Example input and output
dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXkcode_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
code_challenge_method=S256This is the worked example from the RFC, so it is a good test vector for your own PKCE code. Load it with RFC example.
code_verifier: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
code_challenge: E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw+cM=The code_challenge is not valid
Remove the = padding: an S256 code_challenge is base64url without padding.
It contains + or /, so it is standard Base64. An S256 code_challenge uses base64url (- and _).
This is the right hash in standard Base64. Servers expect base64url without padding: replace + with -, / with _ and drop the =.
Expected code_challenge (S256): E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cMbtoa() and most Base64 helpers produce standard Base64. Converting the result to base64url fixes it.
Authorization endpoint: https://auth.example.com/authorize
client_id: example-client-id
redirect_uri: https://app.example.com/callback
scope: openid profile
state: af0ifjsldkjhttps://auth.example.com/authorize?response_type=code&client_id=example-client-id&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback&scope=openid%20profile&state=af0ifjsldkj&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256The URL carries the challenge, never the verifier. The verifier goes only in the token request body, after the user comes back with a code.
Common errors and fixes
- invalid_grant from the token endpoint (PKCE verification failed)
- The code_verifier you sent does not hash to the code_challenge from the authorization request. Usually the client generated a new verifier after the redirect, lost it on a page reload, or read it from storage in another tab. Store the verifier before redirecting and read the same one back in the callback; Check a pair shows whether the two values belong together.
- Character 32 is “+”, which a code_verifier can’t contain
- The verifier was made with standard Base64, which uses + / and =. A verifier may contain only A–Z, a–z, 0–9 and - . _ ~, so encode the random bytes as base64url: replace + with -, / with _ and drop the = padding.
- Too short: 34 characters. A code_verifier must be 43 to 128 characters long.
- RFC 7636 sets 43 characters as the minimum. Base64url-encode 32 random bytes to get exactly 43 characters, or more bytes for a longer verifier.
- An S256 code_challenge is always 43 characters
- A SHA-256 hash is 32 bytes, which is 43 characters in base64url without padding. A 44-character value ending in = still has padding, and a 64-character value is the hash in hex. Encode the raw hash bytes as base64url.
- invalid_request: code challenge required
- The authorization server requires PKCE for this client and the request had no code_challenge (RFC 7636 §4.4.1). Add code_challenge and code_challenge_method=S256 to the authorization URL.
- invalid_request: transform algorithm not supported
- The server does not accept the code_challenge_method you sent. Use S256, which every PKCE server must support. Don’t fall back to plain after S256 fails: RFC 7636 §7.2 forbids that downgrade, because an error here points to a faulty server or an attacker.
Common use cases
- Testing an OAuth 2.0 or OpenID Connect authorization code flow by hand in a browser, Postman or curl
- Debugging invalid_grant errors when the token endpoint rejects a code_verifier
- Checking that a client library produces the right S256 code_challenge
- Creating fixed test vectors for unit tests of PKCE code
- Learning how PKCE protects single-page apps, mobile apps and CLIs that can’t keep a client secret
Privacy and security notes
- The verifier is made with crypto.getRandomValues and hashed with Web Crypto in this browser tab. Nothing you generate or paste is sent to DevPrix or stored on a server.
- Share copies a link without your values, and Send to is not offered: the verifier is never placed in a URL. Reloading the page creates a new pair.
- Values you copy from here are for testing. In production the client generates the verifier for each sign-in and discards it after the token request.
Frequently asked questions
Why do I need PKCE?
An authorization code travels through the browser, where it can leak through logs, history or a malicious app registered for the same redirect URI. PKCE binds the code to the client that started the flow, so a stolen code is useless without the verifier. The OAuth 2.0 Security Best Current Practice (RFC 9700) says public clients MUST use PKCE and recommends it for confidential clients too.
Why S256 and not plain?
With plain, the challenge is the verifier itself, so anyone who can read the authorization request can redeem the code. RFC 7636 says plain SHOULD NOT be used in new implementations, and RFC 9700 says clients SHOULD use a method that does not expose the verifier; currently S256 is the only one. Use plain only if a server cannot support S256.
How long should the code_verifier be?
Between 43 and 128 characters. RFC 7636 recommends 32 random bytes encoded as base64url, which gives 43 characters and 256 bits of randomness. That is already far beyond guessing; longer verifiers are allowed but add little.
Does PKCE replace the state parameter?
It can protect against cross-site request forgery, but only if you know the authorization server supports PKCE; otherwise state or the OpenID Connect nonce must do that job (RFC 9700 §4.7.1). Many apps keep a random state anyway, which is why the example request includes one.
Where should my app keep the verifier?
Somewhere only your client can read until the callback: memory, sessionStorage in a single-page app, or the app’s private storage on mobile. Delete it after the token request, and create a new one for every authorization request.