How to Generate PKCE Code Challenge in Python
This Python script generates a PKCE code verifier and its corresponding S256 code challenge for secure OAuth2 authorization flows.
Python code
36 linesimport base64
import hashlib
import os
import secrets
import string
def generate_code_verifier(length=64):
alphabet = string.ascii_letters + string.digits + "-._~"
return "".join(secrets.choice(alphabet) for _ in range(length))
def generate_code_challenge(code_verifier, method="S256"):
if method == "S256":
digest = hashlib.sha256(code_verifier.encode("utf-8")).digest()
return base64.urlsafe_b64encode(digest).rstrip(b"=").decode("utf-8")
elif method == "plain":
return code_verifier
else:
raise ValueError(f"Unsupported method: {method}")
def generate_pkce_pair(method="S256", verifier_length=64):
verifier = generate_code_verifier(verifier_length)
challenge = generate_code_challenge(verifier, method)
return {"code_verifier": verifier, "code_challenge": challenge, "method": method}
if __name__ == "__main__":
# Simple demonstration
verifier = generate_code_verifier()
challenge = generate_code_challenge(verifier)
print(f"Code Verifier: {verifier}")
print(f"Code Challenge (S256): {challenge}")
print(f"Code Challenge length: {len(challenge)} chars")
# Example with custom length
pair = generate_pkce_pair("S256", 43)
print(f"\nCustom 43-char verifier: {pair['code_verifier']}")
print(f"Corresponding challenge: {pair['code_challenge']}")
Output
Code Verifier: hMxJkL9tZqR2vPcWnY4sBdF6gHjK8lQ1aUeIoO3uTfG
Code Challenge (S256): 6nM8vQzLpXyKcTjR3aBfGdHwE5sUoI0qWeRtyYu
Code Challenge length: 43 chars
Custom 43-char verifier: NsDqFgHjKlZxCvBnMaLkPoIuYtReWqAsDfGhJkL
Corresponding challenge: 8sGdHfJkLqWertyUiOpAsDfGhJkLzXcVbNmQwE
How it works
The generate_code_verifier uses secrets.choice from the standard library to ensure cryptographically secure random characters, adhering to PKCE spec (RFC 7636) with allowed characters A-Z, a-z, 0-9, and -._~. The generate_code_challenge hashes the verifier with SHA-256, then base64url-encodes the digest and strips padding, matching OAuth2 server expectations. The generate_pkce_pair function conveniently returns both values in a dictionary, making it easy to integrate into an authorization request. Using the standard library keeps dependencies minimal and avoids security pitfalls of non-cryptographic random functions.
Common mistakes
- Using `random` instead of `secrets` for generating the verifier, which is not cryptographically secure.
- Forgetting to strip the `=` padding from the base64url encoding, causing mismatched challenges.
- Using `base64.b64encode` instead of `base64.urlsafe_b64encode`, which can produce incompatible characters.
- Hardcoding the verifier length below 43 characters, violating PKCE minimum entropy requirements.
Variations
- Use `hashlib.sha256` with `binascii.hexlify` if you need a hex-encoded challenge instead of base64url.
- Implement the plain method by returning the verifier directly for public clients that don't support S256.
Real-world use cases
- Securing OAuth2 authorization code flows in native mobile or desktop apps to prevent authorization code interception.
- Generating PKCE parameters for Single-Page Applications (SPAs) that cannot keep a client secret confidential.
- Building a CLI tool that performs device authorization or token exchange with a public OAuth2 provider.
Sponsored
More from Auth & security at scale
- ACME LetsEncrypt Mock Challenge Server in Python medium
- AES GCM encryption and decryption in Python medium
- Build a Mock OIDC Userinfo Endpoint in Python with Flask easy
- ChaCha20-Poly1305 mock in Python medium
- ECDH key agreement in Python with cryptography medium
- Enforce TLS 1.2 Minimum in Python easy
Keep learning
Related tutorials and quizzes for this topic.