plain is not accepted — only S256. Build it in from the start; it is not something you can add later.
This applies to the authorization code flow only. The device flow has no
code_challenge — the device code itself is the secret, and it is bound to your client_id on the server. Nothing on this page applies there.The two values
The point is that the challenge is useless on its own. Someone who intercepts the redirect and steals your authorization code still cannot exchange it, because they cannot produce the verifier that hashes to the challenge you registered.
Generating them
The verifier must be 43 to 128 characters, using onlyA-Z a-z 0-9 - . _ ~. The usual approach is 32 random bytes, base64url-encoded, which lands at 43 characters.
Using them
Send the challenge on the authorize redirect:Authorization: Basic or a client_secret parameter; see the authorization code guide for the two variants side by side.
What goes wrong
The one exception:
401 invalid_client does not consume the code. Client authentication runs before the code is touched, so a request rejected for a missing or wrong client_secret never reaches it.That matters while you are getting a server app’s credentials right: the code survives, and you can retry the same one once the secret is fixed, as long as you are inside the 10-minute window.A malformed verifier reports That is the same error as a genuinely mismatched verifier, so the message will not tell you which of the two happened. If PKCE verification fails, check the length and alphabet of your verifier before assuming your hashing is wrong.
invalid_grant, not invalid_request. If the verifier is shorter than 43 characters, longer than 128, or contains a character outside A-Z a-z 0-9 - . _ ~, the error you get back is:- Standard base64 instead of base64url.
+and/must be-and_. - Leaving the
=padding on. Strip it from both values. - Hashing the wrong thing. The challenge is the SHA-256 of the verifier string, not of the random bytes you generated it from.
dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk, the challenge must be E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM. That pair is the worked example from RFC 7636 Appendix B, so any correct implementation reproduces it.
See also
OAuth flow
The full walkthrough this page plugs into.
AUTH V2 overview
Client types, token lifetimes, and which flow to use.
Migrating from V1
Porting existing code, including what changes about PKCE.
Scopes
What to put in the
scope parameter on the authorize URL.