Root cause
Authentication & access control introduced
The AI-assisted change below is where the vulnerable behavior appears.
How AI contributed: Direct introductionPublished May 1, 2025 in
cloudflare/workers-oauth-providerAll 2 findings in this repository
Provisional finding. Full causal and release verification remains incomplete.
An AI-assisted PKCE feature checked a verifier only when an authorization grant stored a challenge. A token request could therefore present a verifier for a grant created without a challenge. The published downgrade flow also requires absent or incorrect client state validation; ordinary grant, client, authentication and redirect checks still apply.
Root cause
The AI-assisted change below is where the vulnerable behavior appears.
Fix
Rejects a token request that supplies code_verifier when the stored authorization grant has no codeChallenge, closing the PKCE downgrade mismatch before any tokens are issued.
Case facts
@@ -695,6 +719,38 @@ ); } + // Verify PKCE code_verifier if code_challenge was provided during authorization+ if (grantData.codeChallenge) {+ if (!codeVerifier) {+ return createErrorResponse(+ 'invalid_request',+ 'code_verifier is required for PKCE'+ );+ }++ // Verify the code verifier against the stored code challenge+ let calculatedChallenge: string;++ if (grantData.codeChallengeMethod === 'S256') {+ // SHA-256 transformation for S256 method+ const encoder = new TextEncoder();+ const data = encoder.encode(codeVerifier);+ const hashBuffer = await crypto.subtle.digest('SHA-256', data);+ const hashArray = Array.from(new Uint8Array(hashBuffer));+ calculatedChallenge = base64UrlEncode(String.fromCharCode(...hashArray));+ } else {+ // Plain method, direct comparison+ calculatedChallenge = codeVerifier;+ }++ if (calculatedChallenge !== grantData.codeChallenge) {+ return createErrorResponse(+ 'invalid_grant',+ 'Invalid PKCE code_verifier'+ );+ }+ }+ // Code is valid - generate tokens const accessTokenSecret = generateRandomString(TOKEN_LENGTH); const refreshTokenSecret = generateRandomString(TOKEN_LENGTH);@@ -1235,6 +1235,11 @@ return this.createErrorResponse('invalid_grant', 'Invalid redirect URI'); } + // Reject if code_verifier is provided but PKCE wasn't used in authorization+ if (!isPkceEnabled && codeVerifier) {+ return this.createErrorResponse('invalid_request', 'code_verifier provided for a flow that did not use PKCE');+ }+ // Verify PKCE code_verifier if code_challenge was provided during authorization if (isPkceEnabled) { if (!codeVerifier) {AI-assisted change dcf4c52fc0f0a1dda6403c48553be96d99d1146647adc0ea3a961108426ab71d · Fix 6832eb4bef842320e8fa7bf1565cfc391a3635fd215a8e30ce07046d33cc0f37