Skip to content

fix(oauth): reject plain PKCE method per OAuth 2.1 spec - #2669

Open
georgelzrc wants to merge 1 commit into
supabase:masterfrom
georgelzrc:fix/oauth21-reject-plain-pkce
Open

fix(oauth): reject plain PKCE method per OAuth 2.1 spec#2669
georgelzrc wants to merge 1 commit into
supabase:masterfrom
georgelzrc:fix/oauth21-reject-plain-pkce

Conversation

@georgelzrc

Copy link
Copy Markdown

Problem

validatePKCEParams() in authorize.go accepts code_challenge_method=plain.
VerifyPKCEChallenge() in security/pkce.go verifies it by comparing
code_challenge == code_verifier with constant-time compare.

OAuth 2.1 (draft-ietf-oauth-v2-1-12, §4.1.1) explicitly removes support for plain:

"The plain code challenge method is not supported in OAuth 2.1."

Why it matters

With plain, code_challenge = code_verifier is sent unencrypted in the
authorization request URL (GET /oauth/authorize?code_challenge=<value>&...).

This URL routinely appears in:

  • Server access logs
  • Browser history
  • Referer headers on the redirect to the client
  • CDN and proxy logs

An attacker who can read any of those can immediately replay the authorization
code by supplying code_verifier = code_challenge in the token exchange,
defeating PKCE's proof-of-possession guarantee entirely.

With S256 this is impossible: code_challenge = BASE64URL(SHA256(code_verifier))
is a one-way transform.

Changes

  • internal/api/oauthserver/authorize.govalidatePKCEParams(): reject any
    code_challenge_method other than S256 with a clear error message referencing
    the spec.
  • internal/security/pkce.goVerifyPKCEChallenge(): remove the plain
    case so it falls through to default returning PKCEInvalidCodeMethodError.
  • internal/security/pkce_test.go: update all plain cases to expect errors.
  • internal/api/oauthserver/authorize_test.go: add TestValidatePKCEParams_OAuth21
    covering S256 accepted, plain/PLAIN/rs256 rejected, and missing-param cases.

References

OAuth 2.1 (draft-ietf-oauth-v2-1-12, Section 4.1.1) explicitly removes
support for the plain code_challenge_method. Only S256 is permitted.

With plain, code_challenge == code_verifier, and code_challenge is
transmitted in the authorization request URL, which routinely appears
in server access logs, browser history, Referer headers, and CDN logs.
An attacker with read access to any of those can immediately replay the
authorization code using code_verifier = code_challenge, defeating
PKCE's proof-of-possession guarantee entirely.

Changes:
- validatePKCEParams(): reject any method other than S256
- VerifyPKCEChallenge(): remove the plain case (falls through to default
  which returns PKCEInvalidCodeMethodError)
- pkce_test.go: update plain cases to expect errors
- authorize_test.go: add TestValidatePKCEParams_OAuth21

Ref: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-12#section-4.1.1
@georgelzrc
georgelzrc requested a review from a team as a code owner August 2, 2026 21:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant