PKCE generator

RFC 7636 S256. Randomness from crypto.getRandomValues. Nothing is uploaded.

By Cobra · Last updated

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

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

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.