{"record":{"id":"00b44de45b20ebfd","repo":"thephpleague/oauth2-server","slug":"access-token-could-not-be-verified","errorCode":null,"errorMessage":"Access token could not be verified","messagePattern":"Access token could not be verified","errorType":"http","errorClass":"OAuthServerException","httpStatus":401,"severity":"error","filePath":"src/AuthorizationValidators/BearerTokenValidator.php","lineNumber":121,"sourceCode":"        $jwt = trim((string) preg_replace('/^\\s*Bearer\\s/i', '', $header[0]));\n\n        if ($jwt === '') {\n            throw OAuthServerException::accessDenied('Missing \"Bearer\" token');\n        }\n\n        try {\n            // Attempt to parse the JWT\n            $token = $this->jwtConfiguration->parser()->parse($jwt);\n        } catch (Exception $exception) {\n            throw OAuthServerException::accessDenied($exception->getMessage(), null, $exception);\n        }\n\n        try {\n            // Attempt to validate the JWT\n            $constraints = $this->jwtConfiguration->validationConstraints();\n            $this->jwtConfiguration->validator()->assert($token, ...$constraints);\n        } catch (RequiredConstraintsViolated $exception) {\n            throw OAuthServerException::accessDenied('Access token could not be verified', null, $exception);\n        }\n\n        if (!$token instanceof UnencryptedToken) {\n            throw OAuthServerException::accessDenied('Access token is not an instance of UnencryptedToken');\n        }\n\n        $claims = $token->claims();\n\n        // Check if token has been revoked\n        if ($this->accessTokenRepository->isAccessTokenRevoked($claims->get('jti'))) {\n            throw OAuthServerException::accessDenied('Access token has been revoked');\n        }\n\n        // Return the request with additional attributes\n        return $request\n            ->withAttribute('oauth_access_token_id', $claims->get('jti'))\n            ->withAttribute('oauth_client_id', $claims->get('aud')[0])\n            ->withAttribute('oauth_user_id', $claims->get('sub'))","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/thephpleague/oauth2-server/blob/9d2f6fc0a0b5aa1bb02506971d3a4ecff2c6526c/src/AuthorizationValidators/BearerTokenValidator.php#L103-L139","documentation":"The JWT was parsed but failed Lcobucci JWT validation constraints (signature verification, expiry, audience, etc.). Thrown as access_denied('Access token could not be verified') wrapping RequiredConstraintsViolated.","triggerScenarios":"Token signed with a key different from the server's cryptographic key; expired token (outside leeway); audience/issuer mismatch; tampered token.","commonSituations":"Rotated private key on server while clients hold old tokens; clock skew between issuer and resource server; copying tokens across environments (staging token used in production).","solutions":["Ensure the same signing/public key pair used to issue tokens is configured in the resource server's AuthorizationServer/BearerTokenValidator.","Re-issue a fresh access token and retry (expiry or signature mismatch).","Check clock skew; add leeway if servers' clocks drift.","Verify environment (keys, issuer/audience) matches where the token was issued."],"exampleFix":"// before\n// resource server uses stale public key\n$server = new AuthorizationServer(..., new CryptKey('file://old-public.pem'));\n// after\n$server = new AuthorizationServer(..., new CryptKey('file://current-public.pem', null, false));","handlingStrategy":"try-catch","validationCode":"// no pre-call validation possible for signature/expiry; ensure fresh token issued with matching keys\nif (time() >= $claimsExpiryFromLocalDecode) { $accessToken = $this->refresh(); }","typeGuard":null,"tryCatchPattern":"try { $request = $validator->validateAuthorization($request); } catch (OAuthServerException $e) {\n  // 401 + WWW-Authenticate: Bearer so client refreshes the token\n  return $e->generateHttpResponse(new Response(), 401);\n}","preventionTips":["Keep issuer/resource-server key pairs in sync (shared config/secrets manager)","Deploy tokens per environment; never share across envs","Set modest token TTLs so clients rely on refresh flow"],"tags":["oauth","jwt","signature-verification"],"backgroundTag":"jwt-signature-invalid","analyzedSha":"9d2f6fc0a0b5aa1bb02506971d3a4ecff2c6526c","analyzedAt":"2026-09-15T22:33:30.452Z","contentChangedAt":"2026-09-15T22:33:30.452Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}