PKCE Generator
Generate an RFC 7636 code_verifier and S256 code_challenge, see why a challenge fails, and copy the authorize URL and token cURL. Nothing leaves your browser.
- Runs in your browser
- Your data never leaves your browser
- Free · No Sign-Up
Scan with WeChat to share this tool
Examples, details and FAQ Worked examples, how it compares with other tools, and answers to common questions.
The verifier and challenge follow RFC 7636 §4.1 and §4.2: code_challenge = BASE64URL(SHA256(ASCII(code_verifier))), base64url without = padding. The generator turns n random bytes into base64url and keeps the first characters you asked for; 43 characters use exactly 32 bytes (256 bits), the amount RFC 7636 §7.1 recommends. The SHA-256 here is the same function as in the Hash Generator; the difference is the base64url encoding of the raw 32 digest bytes instead of hex.
Examples You Can Reproduce
RFC 7636 Appendix B. Paste dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk into the verifier field. The tool shows E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM, the value in Appendix B. The verifier is the base64url form of the 32 octets listed there, starting 116, 24, 223, 180. If a library gives you a different challenge for this verifier, the library is wrong.
An authorization URL. With endpoint https://auth.example.com/authorize, client_id s6BhdRkqt3, redirect_uri https://client.example.org/cb, scope openid profile, state xyz and nonce n-0S6_WzA2Mj, the tool builds:
https://auth.example.com/authorize?response_type=code&client_id=s6BhdRkqt3&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb&scope=openid+profile&state=xyz&nonce=n-0S6_WzA2Mj&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256
Values are form-encoded as RFC 6749 Appendix B requires, so the space in the scope becomes +. code_challenge_method is always written out, because RFC 7636 §4.3 makes a missing method mean plain. To read a callback URL back into its parts, use the URL Parser.
A verifier copied from standard Base64. Paste dBjftJeZ4CVP+mB92K27uhbUJU1p1r/wW1gFWFOEjXk= and the tool stops with: Character 13 ”+” is not allowed. A verifier may only contain A–Z, a–z, 0–9 and - . _ ~ (RFC 7636 §4.1). followed by how to convert it to base64url. Spaces, line breaks and non-ASCII characters get their own hint, and lengths outside 43–128 are reported with the actual count.
Finding a Challenge That Does Not Match
When the token endpoint answers invalid_grant, the usual cause is a challenge built the wrong way. Open Check a code_challenge and paste what your app sent. For the RFC verifier above, these are the answers:
| Pasted code_challenge | Result |
|---|---|
E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM | Match |
E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM= | Padding: remove the = |
E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw+cM= | Standard Base64 instead of base64url |
13d31e961a1ad8ec2f16b10c4c982e0876a878ad6df144566ee1894acb70f9c3 | Hex digest instead of base64url |
dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk | The verifier itself (plain) |
38v3YOi6zQgk1xkqk6Y5dvSDoBHqZrTh3mmWHxxWvyk | SHA-256 of the decoded random bytes, not of the verifier string |
The last row is a real bug pattern: code that keeps the 32 random bytes and hashes them, instead of hashing the 43 ASCII characters it sent as the verifier. With plain selected, the check also recognizes the S256 value and tells you to send code_challenge_method=S256. For the Base64 side of these errors, base64url is RFC 4648 §5: - and _ instead of + and /, and PKCE drops the padding.
The Same Calculation in Code
These functions give the RFC challenge above. The tests for this page run all three.
// Browsers and Node.js 20+ (Web Crypto)
const base64url = (bytes) =>
btoa(String.fromCharCode(...bytes)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
function createVerifier() {
return base64url(crypto.getRandomValues(new Uint8Array(32))); // 43 characters
}
async function createChallenge(verifier) {
const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));
return base64url(new Uint8Array(digest));
}
import base64
import hashlib
import secrets
def create_verifier() -> str:
return secrets.token_urlsafe(32) # 32 random bytes -> 43 characters
def create_challenge(verifier: str) -> str:
digest = hashlib.sha256(verifier.encode("ascii")).digest()
return base64.urlsafe_b64encode(digest).rstrip(b"=").decode("ascii")
package main
import (
"crypto/rand"
"crypto/sha256"
"encoding/base64"
)
func createVerifier() string {
b := make([]byte, 32)
rand.Read(b)
return base64.RawURLEncoding.EncodeToString(b) // 43 characters
}
func createChallenge(verifier string) string {
sum := sha256.Sum256([]byte(verifier))
return base64.RawURLEncoding.EncodeToString(sum[:])
}
In Go, golang.org/x/oauth2 also has GenerateVerifier, S256ChallengeOption and VerifierOption. After login, the JWT Decoder shows the claims of the ID token, including the nonce.
How It Differs from Other Online PKCE Generators
Tested on 2026-10-01 in desktop Chromium, with the page’s fetch, XMLHttpRequest and sendBeacon recorded while generating and checking:
- tonyxu-io.github.io/pkce-generator (first on Bing for “pkce generator”) runs locally and gets the RFC example right, but it does not validate input: a 42-character verifier, a verifier with
+,/and=, Chinese text and an empty field all produce a challenge. The empty field gives47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU, the SHA-256 of nothing. - Ping Identity’s PKCE Code Generator generates locally, but the verifier field is read-only, so you cannot check a verifier from your own app.
- Elysia Tools posts the mode, the verifier and the challenge as JSON to
api.elysiatools.comand shows the result the server returns, for generating as well as checking. - AuthAction, Found Tools, encode64 and ToolBada generate locally but cannot check an existing verifier or challenge.
This tool checks a pasted verifier against the RFC grammar and points at the character that breaks it, and tells you why a challenge does not match instead of only that it does not. The verifier never leaves the tab.
Limits
- Methods are S256 and plain, the two that RFC 7636 defines. Generated verifiers use the 64 base64url characters, so
.and~(valid in a verifier) never appear in them. - The entropy figure is shown only for verifiers this page generated. For a pasted verifier the tool can check the format, not how random it is.
- The tool does not contact your authorization server. It cannot exchange the code for you; run the cURL command, which is written for a public client. Confidential clients add their client authentication.
- Provider rules beyond RFC 7636 are not checked. For example, LINE Login accepts only S256, and Kakao and WeChat separate scopes with commas (the nonce is still added when such a scope contains
openid). - A verifier belongs to one authorization request. Generate a new one for every login; reusing a fixed verifier defeats the protection, and RFC 9700 §2.1.1 encourages servers to detect and refuse constant values.
FAQ
Is the verifier sent anywhere or saved?
No. Random bytes come from crypto.getRandomValues and SHA-256 from crypto.subtle in this tab. The tool makes no network requests, does not write the verifier to localStorage, sessionStorage, cookies or the URL, and the page loads no analytics or ad scripts. Reloading the page discards the verifier.
How long should the code_verifier be?
43 characters from 32 random bytes is the recipe in RFC 7636 sections 4.1 and 7.1, and it gives the 256 bits of entropy the RFC asks for. Longer verifiers (up to 128 characters) are allowed; 256 bits is already the target the RFC sets. A verifier you type by hand is valid if it has 43 to 128 characters from A-Z, a-z, 0-9 and - . _ ~, but its strength depends on how it was made.
Should I use S256 or plain?
S256. RFC 7636 section 4.2 says a client that can compute SHA-256 must use S256, RFC 9700 says S256 is currently the only method that does not expose the verifier in the authorization request, and the OAuth 2.1 draft removes plain. Some providers, such as LINE Login, accept only S256.
Why does my token request fail with invalid_grant?
Paste the code_challenge your app sent into Check a code_challenge. The tool tells you whether it matches the verifier or was built in a known wrong way: Base64 with + / =, hex, trailing padding, the verifier itself, or a hash of the decoded random bytes. If it matches, check that code_challenge_method=S256 was sent (without it the server assumes plain), that the verifier comes from the same login attempt, and that the code was used only once.
Do confidential clients need PKCE too?
RFC 9700 (OAuth 2.0 Security Best Current Practice, 2025) requires PKCE for public clients and recommends it for confidential clients, because it also stops authorization code injection. A confidential client still authenticates at the token endpoint; add its client secret or other credentials to the cURL command yourself.