PKCE (Proof Key for Code Exchange)
PKCE, defined in RFC 7636, closes a gap in the Authorization Code Grant for clients that can't hold a secret — single-page apps, mobile and native apps, anything running on a device you don't control. Without it, an attacker who intercepts the authorization code (via a malicious app registered on the same custom URL scheme, for instance) can redeem it for tokens themselves.
How it works
- Before redirecting to
/authorize, the client generates a randomcode_verifierand derives acode_challengefrom it (either the verifier itself,plain, or its SHA-256 hash,S256). - The client sends
code_challengeandcode_challenge_methodon the/authorizerequest. The authorization server stores them alongside the issued code. - When redeeming the code at
/token, the client sends the originalcode_verifier. The server re-derives the challenge from it and checks it matches what was stored.
An attacker who only intercepts the code from the redirect can't complete step 3 without the code_verifier, which never left the original client.
PKCE in AuthKit
PKCE is implemented as an extension (rfc7636) rather than being hard-wired into the Authorization Code flow — see Extension System for why. It implements AuthorizationRequestValidator (to require and store the challenge) and TokenRequestValidator (to verify the verifier at redemption).
pkce := rfc7636.New(
rfc7636.NewOptions().SetAllowPlain(false),
)
flow, _ := authorizationcode.Must(
authorizationcode.NewConfig().
SetClientManager(clientMgr).
SetAuthCodeManager(authCodeMgr).
SetTokenManager(tokenMgr).
RegisterExtension(pkce),
)plain vs S256
RFC 7636 allows both the plain method (challenge equals verifier) and S256 (challenge is the SHA-256 hash of the verifier). AuthKit accepts both by default. plain exists mostly for constrained clients that can't compute SHA-256, and offers weaker protection since the challenge and verifier are identical — anyone who observes the /authorize request sees the value they'd need at /token.
RFC 9700 (OAuth 2.0 Security Best Current Practice) recommends requiring S256 and rejecting plain outright. To do that in AuthKit:
pkce := rfc7636.New(
rfc7636.NewOptions().SetAllowPlain(false),
)Unless you have a specific client that genuinely cannot compute SHA-256, set AllowPlain(false).
Should confidential clients use PKCE too?
RFC 9700 recommends PKCE for all Authorization Code clients, not only public ones — it protects against authorization code injection regardless of whether the client can also authenticate with a secret. There's no reason to register the extension conditionally based on client type; register it once for the flow and let every client go through it.
See also
- OpenID Connect — PKCE and OIDC are commonly registered together on the same flow.
- RFC 7636 API Reference — package-level type reference.