PKCE generator
- Runs on your device
- Nothing uploaded
- Free, no sign-up
RFC 7636 S256. Randomness from crypto.getRandomValues. Nothing is uploaded.
How PKCE S256 works
PKCE protects public OAuth clients. You create a high-entropy code_verifier, then a code_challenge = BASE64URL(SHA-256(verifier)) without padding. The authorize request sends the challenge and method S256. The token request later proves possession of the verifier.
This page uses the Web Crypto API locally. It does not talk to your IdP and does not store verifiers.
Worked example: what a verifier and challenge look like
A verifier might look like a 43+ character URL-safe string. Its S256 challenge is a different URL-safe string derived from the hash. If either value is truncated or re-encoded with standard Base64 (+//), the IdP will reject the token exchange.
Copy verifier and challenge into your app config or test harness as a pair. Regenerating either alone breaks the pair.
Limits
Plain method (challenge = verifier) is outdated and weaker; this tool focuses on S256. It does not build full authorize URLs, manage cookies, or rotate secrets for confidential clients. Use your IdP docs for client_id, redirect_uri, and scope.
Pairing with authorize URLs
Your authorize request needs code_challenge, code_challenge_method=S256, client_id, redirect_uri, and response_type=code (plus scope and state as required). The token request later sends the matching code_verifier. This page only builds the PKCE pair; it does not assemble the full URL for your IdP.
Worked example: the RFC 7636 test vector
RFC 7636 gives a test pair: verifier dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk produces the S256 challenge E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM. The challenge is the Base64URL (no padding) SHA-256 of the verifier. Send the challenge with the authorization request and the verifier with the token request.
Pitfalls
- Verifier length. It must be 43 to 128 characters from A-Z, a-z, 0-9, and
-._~. - Padding. The challenge must not end in
=. Standard Base64 with padding fails. - Plain method.
plainsends the verifier as the challenge. Use S256 unless a server truly cannot. - Reusing verifiers. Make a new verifier for every authorization request.
Build a PKCE verifier and S256 challenge in code
The verifier is random base64url text; the challenge is the base64url SHA-256 of the verifier, without padding.
JavaScript: Web Crypto, no dependencies
// Browsers and Node 20+: Web Crypto only, no package needed
const b64url = (bytes) =>
btoa(String.fromCharCode(...bytes)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
const verifier = b64url(crypto.getRandomValues(new Uint8Array(32))); // 43 chars
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
const challenge = b64url(new Uint8Array(digest));
console.log({ verifier, challenge, code_challenge_method: "S256" });
Python: standard library only
import base64, hashlib, secrets
verifier = secrets.token_urlsafe(32) # 43 chars from the unreserved set
digest = hashlib.sha256(verifier.encode("ascii")).digest()
challenge = base64.urlsafe_b64encode(digest).rstrip(b"=").decode("ascii")
print(verifier, challenge, "S256")
Go: golang.org/x/oauth2
package main
import (
"fmt"
"golang.org/x/oauth2" // go get golang.org/x/oauth2
)
func main() {
verifier := oauth2.GenerateVerifier() // 32 random bytes, base64url, 43 chars
challenge := oauth2.S256ChallengeFromVerifier(verifier)
fmt.Println(verifier, challenge)
// With an oauth2.Config, the library adds the parameters for you:
// url := cfg.AuthCodeURL(state, oauth2.S256ChallengeOption(verifier))
// tok, err := cfg.Exchange(ctx, code, oauth2.VerifierOption(verifier))
}
Each snippet was run on October 14, 2026 with Node.js 20, Python 3.14 and golang.org/x/oauth2 v0.37.0.
PKCE specs: RFC 7636 and related
- RFC 7636: Proof Key for Code Exchange (PKCE)
- RFC 7636, section 4.1: code_verifier: 43 to 128 characters from the unreserved set
- RFC 7636, appendix B: S256 example: the test vector to check your code against
- RFC 9700: OAuth 2.0 Security Best Current Practice: requires PKCE for public clients and recommends it for confidential ones
- RFC 4648, section 5: base64url
- Go: oauth2.GenerateVerifier
FAQ
What is PKCE?
Proof Key for Code Exchange. A verifier stays with the client; a derived challenge goes on the authorize request so an intercepted code is useless without the verifier.
Does this upload verifiers?
No. Generation and hashing run in your browser only.
Why S256 instead of plain?
S256 hashes the verifier so the challenge is not reversible. Plain PKCE is discouraged for new apps.
How long should the verifier be?
RFC 7636 allows 43 to 128 characters from an unreserved URL-safe alphabet. This page targets a strong length in that range.
Do confidential clients need PKCE?
OAuth 2.1 drafts and current security best practice recommend PKCE for all clients using the authorization code flow, not only mobile and single-page apps.
Why does the token request fail with invalid_grant?
Often the verifier sent does not match the one used for the challenge, or the code was already used. Store the verifier per request and send it unchanged.