thephpleague/oauth2-server · error · OAuthServerException
invalid_client
invalid_client
Error message
Client authentication failed
What it means
OAuthServerException::invalidClient signals failed client authentication. In ImplicitGrant it is thrown when the client has no usable redirect URI: an empty redirectUri, or an array of redirect URIs containing more/less than exactly one entry. Since the implicit flow redirects responses, the grant requires a single, reliable registered redirect URI.
Solutions
- Fix your ClientRepository/getClientEntity to return a client whose redirectUri is a single non-empty string for implicit flow clients
- If storing multiple redirect URIs, return only the one matching the request instead of the whole array
- Update the client record so redirect_uri is populated in your persistence layer
- Alternatively use the auth-code grant, which supports redirect URI validation differently
Example fix
// before
public function getClientEntity($id, ...): ClientEntityInterface {
$client->setRedirectUri($allRedirectUris); // array of 3 URIs
}
// after
$client->setRedirectUri('https://app.example.com/callback'); // single exact URI for implicit clients Defensive patterns
Strategy: validation
Validate before calling
$client = $clientRepository->getClientEntity($clientId, 'implicit', null, false);
$uri = $client->getRedirectUri();
if ($uri === '' || (is_array($uri) && count($uri) !== 1)) {
throw new \RuntimeException('Implicit-flow clients need exactly one non-empty redirect URI');
} Type guard
function hasUsableRedirectUri(ClientEntityInterface $c): bool {
$u = $c->getRedirectUri();
return is_string($u) && $u !== '' || (is_array($u) && count($u) === 1);
} Try / catch
try {
$authRequest = $server->validateAuthorizationRequest($request);
} catch (OAuthServerException $e) {
if ($e->getErrorType() === 'invalid_client') {
// show error page; fix client record's redirect_uri
}
throw $e;
} Prevention
- Return a single redirect URI string from getClientEntity for implicit clients
- Add DB constraints ensuring redirect_uri is non-empty for registered clients
- Validate client registration data at write time, not only at authorization time
When it happens
Trigger: validateAuthorizationRequest -> getClientEntityOrFail succeeds but the client entity's getRedirectUri() returns '' or an array with count !== 1; the grant emits CLIENT_AUTHENTICATION_FAILED and throws invalidClient.
Common situations: Client entity returned by a custom ClientRepository has an empty redirect_uri field; developer returns all registered redirect URIs as an array when the implicit grant expects exactly one; database row with NULL/blank redirect_uri; migrate from another grant type that tolerated multiple redirect URIs.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
AI-assisted analysis of thephpleague/oauth2-server@9d2f6fc0a0 (2026-09-15).
Data as JSON: /api/errors/8b08f7a9e2f2f256.
Report an issue: GitHub.
Appendix: source
Thrown at src/Grant/ImplicitGrant.php:117
$this->getServerParameter('PHP_AUTH_USER', $request)
);
if (is_null($clientId)) {
throw OAuthServerException::invalidRequest('client_id');
}
$client = $this->getClientEntityOrFail($clientId, $request);
$redirectUri = $this->getQueryStringParameter('redirect_uri', $request);
if ($redirectUri !== null) {
$this->validateRedirectUri($redirectUri, $client, $request);
} elseif (
$client->getRedirectUri() === '' ||
(is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1)
) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient($request);
}
$stateParameter = $this->getQueryStringParameter('state', $request);
$scopes = $this->validateScopes(
$this->getQueryStringParameter('scope', $request, $this->defaultScope),
$this->makeRedirectUri(
$redirectUri ?? $this->getClientRedirectUri($client),
$stateParameter !== null ? ['state' => $stateParameter] : [],
$this->queryDelimiter
)
);
$authorizationRequest = $this->createAuthorizationRequest();
$authorizationRequest->setGrantTypeId($this->getIdentifier());
$authorizationRequest->setClient($client);
$authorizationRequest->setRedirectUri($redirectUri);
View on GitHub (pinned to 9d2f6fc0a0)