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

  1. Fix your ClientRepository/getClientEntity to return a client whose redirectUri is a single non-empty string for implicit flow clients
  2. If storing multiple redirect URIs, return only the one matching the request instead of the whole array
  3. Update the client record so redirect_uri is populated in your persistence layer
  4. 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

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

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)