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

  1. Obtain a new access token (re-authenticate or use a valid refresh token).
  2. Clear cached tokens on logout/revocation client-side.
  3. Check isAccessTokenRevoked() implementation — a DB issue can falsely report revocation.
  4. 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

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


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)