thephpleague/oauth2-server · error · OAuthServerException
Access token has been revoked
Error message
Access token has been revoked
What it means
Guard inside validateAuthorization(): after the JWT signature and standard constraints (expiry, audience, etc.) pass, the token's jti claim is looked up against the access-token repository and the stored token has been revoked (e.g. via refresh-token rotation, explicit revoke endpoint, or token revocation). The credential is valid JWT-wise but no longer active, so the request is denied.
Solutions
- Obtain a new access token (re-authenticate or use a valid refresh token).
- Clear cached tokens on logout/revocation client-side.
- Check isAccessTokenRevoked() implementation — a DB issue can falsely report revocation.
- Verify revocation persistence and that the right token identifier (jti) is stored.
Example fix
// before
$request->getHeader('Authorization'); // reuses cached revoked token
// after
if ($this->tokenStore->isRevoked($accessToken)) {
$accessToken = $this->refreshAccessToken($refreshToken);
}
$request = $request->withHeader('Authorization', 'Bearer ' . $accessToken); Defensive patterns
Strategy: try-catch
Validate before calling
// can't pre-check server-side revocation; client should handle 401 by refreshing
if ($this->localRevocationCache->contains($jti)) { $this->forceRefresh(); } Try / catch
try { $request = $validator->validateAuthorization($request); } catch (OAuthServerException $e) {
// signal the client to discard the token and refresh
return $e->generateHttpResponse(new Response(), 401);
} Prevention
- Purge client token cache on logout and on any 401
- Treat 401 as refreshable but refresh-token reuse failures as full re-login
- Keep isAccessTokenRevoked backed by a reliable store (DB index on jti)
When it happens
Trigger: Token was revoked (logout, client revoked consent, password change, refresh-token rotation revoking ancestors) but the client keeps replaying it.
Common situations: User logged out and old token cached client-side; refresh token reuse triggered revocation of the family; stale token cached in a mobile app.
Related errors
- Missing "Authorization" header
- Missing "Bearer" token
- invalid request: response_type
- unsupported grant type
- Access token could not be verified
AI-assisted analysis of thephpleague/oauth2-server@9d2f6fc0a0 (2026-09-15).
Data as JSON: /api/errors/7b704decc5fe6114.
Report an issue: GitHub.
Appendix: source
Thrown at src/AuthorizationValidators/BearerTokenValidator.php:132
}
try {
// Attempt to validate the JWT
$constraints = $this->jwtConfiguration->validationConstraints();
$this->jwtConfiguration->validator()->assert($token, ...$constraints);
} catch (RequiredConstraintsViolated $exception) {
throw OAuthServerException::accessDenied('Access token could not be verified', null, $exception);
}
if (!$token instanceof UnencryptedToken) {
throw OAuthServerException::accessDenied('Access token is not an instance of UnencryptedToken');
}
$claims = $token->claims();
// Check if token has been revoked
if ($this->accessTokenRepository->isAccessTokenRevoked($claims->get('jti'))) {
throw OAuthServerException::accessDenied('Access token has been revoked');
}
// Return the request with additional attributes
return $request
->withAttribute('oauth_access_token_id', $claims->get('jti'))
->withAttribute('oauth_client_id', $claims->get('aud')[0])
->withAttribute('oauth_user_id', $claims->get('sub'))
->withAttribute('oauth_scopes', $claims->get('scopes'));
}
}
View on GitHub (pinned to 9d2f6fc0a0)