PKCE
This page helps your app use PKCE, and helps you decide whether a confidential client may go without it.
PKCE (Proof Key for Code Exchange, said “pixy”) ties an authorization code to the app that asked for it. Your app makes up a secret for each sign-in, sends a hash of it with the authorization request, and sends the secret itself when it redeems the code. Whoever intercepts the code, on its way back through the browser, doesn’t have the secret, so the code is no use to them. It’s defined in RFC 7636.
Most OAuth libraries do all of this for you: turn PKCE on and use S256.
Use PKCE
Section titled “Use PKCE”-
Make a code verifier: a random string of 43 to 128 characters, using only
A–Z,a–z,0–9,-,.,_and~. 32 random bytes, base64url-encoded without padding, give 43 of them. Make a new one for every authorization request, and keep it until your app redeems the code. -
Make the code challenge: the SHA-256 hash of the verifier, base64url-encoded without padding. RFC 7636’s own example, which you can test your code against:
Code verifier dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXkCode challenge E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM -
Send the challenge, and
code_challenge_method=S256, with the authorization request:GET /auth/authorize?client_id=my-app&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback&response_type=code&scope=openid%20profile&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&state=... HTTP/1.1Host: auth.example.com -
Send the verifier when you redeem the code:
Terminal window curl -X POST https://auth.example.com/auth/token \-d "grant_type=authorization_code" \-d "client_id=my-app" \-d "code=..." \-d "redirect_uri=https://app.example.com/callback" \-d "code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"A confidential client authenticates as well, with its client secret.
When it’s required
Section titled “When it’s required”PKCE applies to the authorization code flow, the one flow where a code travels through the browser.
- A public client always uses it. A public client has no secret, so the verifier is all that ties the code to it. You can’t turn it off: the admin console doesn’t offer the choice, and the Admin API stores it as required whatever it’s sent. See public and confidential clients.
- A confidential client uses it when the global setting says so, Require PKCE, under Admin, General. It’s on in a new installation.
- A confidential client can override the global setting on its OAuth2 flows tab, under PKCE: Required, Optional (not recommended), or Use the global setting, which a new client starts with.
Optional doesn’t mean ignored: a client that sends PKCE when it’s optional is held to the same rules as one that has to.
What the auth server checks
Section titled “What the auth server checks”At /auth/authorize, code_challenge_method must be S256, and code_challenge must be 43 to 128 characters of the set above. plain isn’t supported, and discovery lists S256 alone in code_challenge_methods_supported. Each failure is refused with invalid_request. When the browser has no session, the user sees the sign-in page first and your app gets the error after it, as The app gets no error back explains.
At /auth/token, the verifier has to match the challenge the code was issued with:
| What’s wrong | error |
error_description |
|---|---|---|
The code was issued with a challenge, and no code_verifier was sent |
invalid_request |
“Missing required code_verifier parameter.” |
| The verifier isn’t 43 to 128 characters of the set above | invalid_grant |
“The code_verifier parameter is incorrect. It should be 43 to 128 characters long and may only contain …” |
| The verifier’s hash isn’t the challenge | invalid_grant |
“Invalid code_verifier (PKCE).” |
The code was issued without a challenge, and a code_verifier was sent |
invalid_request |
“The code_verifier parameter was provided, but PKCE was not used during authorization.” |
| The client is public, and the code was issued without a challenge | invalid_grant |
“This code was issued without PKCE, and public clients are required to use PKCE. …” |
The last one catches a code issued while the client was confidential and PKCE optional, then redeemed after it became public: start a new authorization request.