Developer Tools

PKCE Generator: Code Verifier & Code Challenge

Generate an RFC 7636 PKCE code_verifier and S256 code_challenge for the OAuth 2.0/2.1 authorization code flow.

Method

With plain, the code_challenge is the code_verifier itself. Use it only if your client can't compute SHA-256.

code_verifier
code_verifier appears here.
code_challenge appears here.
state
state appears here.
nonce
nonce appears here.
Validate an existing code_verifier
Build example requests
Authorization request URL
Authorization request URL appears here.
Token request (curl)
Token request (curl) appears here.
Token request (raw HTTP)
Token request (raw HTTP) appears here.

What is PKCE?

PKCE (Proof Key for Code Exchange, pronounced "pixy", defined in RFC 7636) is an extension to the OAuth 2.0 authorization code flow that binds an authorization code to the authorization request that produced it. The client creates a fresh secret code_verifier for every flow and must present it to redeem the code, so a stolen code can't be exchanged for tokens, whether the attacker redeems it directly or injects it into someone else's flow.

Why does OAuth 2.1 require PKCE for confidential clients too?

PKCE is often described as a fix for public clients, like SPAs and mobile apps, that can't keep a client secret. That's a misconception. RFC 7636 was written to stop a malicious native app from stealing and redeeming an authorization code, but shortly after it was published the authorization code injection attack was discovered, and that attack works against confidential clients too.

In a code injection attack, the attacker never needs to break the client's authentication. They start the code flow through the legitimate client application, then tamper with the front-channel redirect from the authorization server so it carries a stolen authorization code. The client redeems that code using its own valid credentials, and the attacker ends up with a session that holds the victim's authorization. Proxy tools such as the Zed Attack Proxy (ZAP) make this trivial.

PKCE defeats the attack: the stolen code is bound to the victim's code_challenge, and the attacker's session holds a different code_verifier, so the token endpoint rejects the exchange. That's why the OAuth 2.0 Security Best Current Practice (RFC 9700) recommends PKCE for every client and OAuth 2.1 makes it a requirement for the authorization code flow. If you use the authorization code flow without PKCE, confidential client or not, you're vulnerable. Always use PKCE.

S256 vs. plain

With S256, the client sends code_challenge = BASE64URL(SHA256(code_verifier)) at the start of the flow, and sends the real code_verifier only at the token exchange, so the verifier is never exposed on the (less-protected) front channel. With plain, code_challenge is just the verifier itself. Always use S256. (To check a base64url value by hand, use the Base64URL decoder.)

How does the PKCE flow work, step by step?

  1. Client generates a new random code_verifier for this authorization request and keeps it with the user's session. RFC 7636 requires 43-128 characters from an unreserved character set (A-Z a-z 0-9 - . _ ~); 64 characters is a reasonable default.
  2. Client derives code_challenge from it (S256) and sends it with the authorization request.
  3. User authenticates and consents at the authorization server.
  4. Authorization server redirects back with an authorization code.
  5. Client exchanges the code for tokens at the token endpoint, sending the original code_verifier.
  6. Authorization server hashes the verifier and compares it to the stored challenge. Tokens are issued only if they match.

How do I enable PKCE in .NET and Duende IdentityServer?

ASP.NET Core's OpenID Connect handler enables PKCE by default (OpenIdConnectOptions.UsePkce = true). On the server side, Duende IdentityServer lets you require it per client with Client.RequirePkce (on by default for new clients): see client configuration in IdentityServer. Keep both turned on for confidential, server-side clients as well: a client secret doesn't protect against code injection.

Computing the S256 challenge server-side in .NET 9+ is a few lines with SHA256.HashData and the new System.Buffers.Text.Base64Url type:

Csharp

using System.Security.Cryptography;
using System.Buffers.Text;

// code_verifier: any RFC 7636-compliant random string, 43-128 chars
var verifierBytes = RandomNumberGenerator.GetBytes(32);
var codeVerifier = Base64Url.EncodeToString(verifierBytes);

// code_challenge = BASE64URL(SHA256(code_verifier))
var challengeBytes = SHA256.HashData(Encoding.ASCII.GetBytes(codeVerifier));
var codeChallenge = Base64Url.EncodeToString(challengeBytes);

Build on Duende IdentityServer

The OpenID Connect and OAuth 2.0 framework for ASP.NET Core, from the team that built this tool.