thephpleague/oauth2-server · error · OAuthServerException
server_error
server_error
Error message
Unsupported code challenge method `%s`
What it means
This server_error is thrown by AuthCodeGrant::validateCodeChallenge when the stored authorization-code payload contains a `code_challenge_method` that the grant does not support (only `plain` and `S256` are allowed). It is a server-side configuration/issue-time problem, not a client verifier mismatch, and intentionally returns HTTP 500-class code `server_error` per RFC 6749 semantics.
Solutions
- Re-issue the authorization code requesting code_challenge_method=S256 (or plain) exactly, with no whitespace or case variants.
- Validate/normalize code_challenge_method at the authorize endpoint before persisting the payload (reject unknown values there).
- Check custom AuthCodePayload serialization/deserialization and any storage middleware that could alter the method string.
- Upgrade league/oauth2-server to a version whose validateAuthorizationCode/authorize path restricts challenge methods to S256/plain.
Example fix
// before: authorize endpoint accepts any method and stores it
$payload->code_challenge_method = $request->getQueryParams()['code_challenge_method'];
// after
$method = $request->getQueryParams()['code_challenge_method'] ?? null;
if (!in_array($method, ['plain', 'S256'], true)) {
throw OAuthServerException::invalidRequest('code_challenge_method');
}
$payload->code_challenge_method = $method; Defensive patterns
Strategy: validation
Validate before calling
// at the authorize endpoint, before storing the payload
$method = $_GET['code_challenge_method'] ?? null;
if (isset($_GET['code_challenge']) && !in_array($method, ['plain', 'S256'], true)) {
throw OAuthServerException::invalidRequest('code_challenge_method');
} Try / catch
try {
$token = $server->respondToAccessTokenRequest($request, $response);
} catch (\League\OAuth2\Server\Exception\OAuthServerException $e) {
if ($e->getCode() === 'server_error' && str_contains($e->getMessage(), 'code challenge method')) {
// log the offending stored method; re-issue code with S256
}
throw $e;
} Prevention
- Whitelist code_challenge_method to S256 (preferred) or plain at the authorize endpoint.
- Never persist the raw query parameter without validation.
- Check custom payload serializers/storage layers do not mutate the method string.
- Prefer S256 everywhere; 'plain' is deprecated by RFC 7636 best practice.
When it happens
Trigger: Exchanging an authorization code whose payload's code_challenge_method is anything other than 'plain' or 'S256' — typically because the authorization request specified code_challenge_method=unsupported-value (e.g. 'S256 ' with whitespace, lowercase variants, or a custom method) and it was stored verbatim, or the payload was hand-crafted/tampered with.
Common situations: A proxy or client SDK sent a non-standard code_challenge_method value that the server persisted; custom storage adapters that lower/uppercase or mutate the method string; forged or stale cached payloads; a server that enabled proof exchange without restricting the method on authorize.
Related errors
AI-assisted analysis of thephpleague/oauth2-server@9d2f6fc0a0 (2026-09-15).
Data as JSON: /api/errors/6a6f35746a0f1115.
Report an issue: GitHub.
Appendix: source
Thrown at src/Grant/AuthCodeGrant.php:186
throw OAuthServerException::invalidRequest(
'code_verifier',
'Code Verifier must follow the specifications of RFC-7636.'
);
}
if (property_exists($authCodePayload, 'code_challenge_method')) {
if (isset($this->codeChallengeVerifiers[$authCodePayload->code_challenge_method])) {
$codeChallengeVerifier = $this->codeChallengeVerifiers[$authCodePayload->code_challenge_method];
if (
!property_exists($authCodePayload, 'code_challenge') ||
!isset($authCodePayload->code_challenge) ||
$codeChallengeVerifier->verifyCodeChallenge($codeVerifier, $authCodePayload->code_challenge) === false
) {
throw OAuthServerException::invalidGrant('Failed to verify `code_verifier`.');
}
} else {
throw OAuthServerException::serverError(
sprintf(
'Unsupported code challenge method `%s`',
$authCodePayload->code_challenge_method
)
);
}
}
}
/**
* Validate the authorization code.
*/
private function validateAuthorizationCode(
stdClass $authCodePayload,
ClientEntityInterface $client,
ServerRequestInterface $request
): void {
if (!property_exists($authCodePayload, 'auth_code_id')) {View on GitHub (pinned to 9d2f6fc0a0)