{"record":{"id":"78a5abee286f4ef7","repo":"spring-projects/spring-security","slug":"invalid-token-78a5ab","errorCode":"invalid_token","errorMessage":"invalid_token","messagePattern":"invalid_token","errorType":"error_code","errorClass":"OAuth2AuthenticationException","httpStatus":401,"severity":"error","filePath":"oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationProvider.java","lineNumber":112,"sourceCode":"\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved authorization with access token\");\n\t\t}\n\n\t\tOAuth2Authorization.Token<OAuth2AccessToken> authorizedAccessToken = authorization.getAccessToken();\n\t\tAssert.notNull(authorizedAccessToken, \"authorizedAccessToken cannot be null\");\n\t\tif (!authorizedAccessToken.isActive()) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t}\n\n\t\tif (!authorizedAccessToken.getToken().getScopes().contains(OidcScopes.OPENID)) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);\n\t\t}\n\n\t\tOAuth2Authorization.Token<OidcIdToken> idToken = authorization.getToken(OidcIdToken.class);\n\t\tif (idToken == null) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Validated user info request\");\n\t\t}\n\n\t\tOidcUserInfoAuthenticationContext authenticationContext = OidcUserInfoAuthenticationContext\n\t\t\t.with(userInfoAuthentication)\n\t\t\t.accessToken(authorizedAccessToken.getToken())\n\t\t\t.authorization(authorization)\n\t\t\t.build();\n\t\tOidcUserInfo userInfo = this.userInfoMapper.apply(authenticationContext);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Authenticated user info request\");\n\t\t}\n\n\t\treturn new OidcUserInfoAuthenticationToken(accessTokenAuthentication, userInfo);","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationProvider.java#L94-L130","documentation":"After confirming the access token is active and carries the openid scope, the OIDC UserInfo authentication provider looks up the OIDC ID Token (OidcIdToken) stored in the same OAuth2Authorization. If none exists, it throws OAuth2AuthenticationException with `invalid_token`, because UserInfo claims must be derived from a valid ID token issued for the authorization. An access token without a corresponding ID token cannot be used at the UserInfo endpoint.","triggerScenarios":"Calling the userInfoEndpoint with an access token issued from a flow that stored no OidcIdToken in the OAuth2AuthorizationService — e.g. an authorization was saved without an id_token, the ID token was removed/expired and pruned via OAuth2AuthorizationService.remove, or a custom TokenGenerator omitted the ID token.","commonSituations":"Access tokens minted through custom grants or client_credentials reused against /userinfo; authorization service implementations (e.g. custom JdbcOAuth2AuthorizationService) that fail to persist the OidcIdToken; tokens surviving after the ID token expired and was invalidated; upgrading authorization-server versions where ID token handling changed.","solutions":["Use an access token obtained from an authorization_code flow that issued an ID token (response includes id_token).","Check that your OAuth2AuthorizationService implementation saves the OidcIdToken alongside the access token when the authorization completes.","Re-run the full authorization flow to create a new OAuth2Authorization containing a current ID token.","If you only need user data without OIDC semantics, expose a normal resource endpoint instead of the UserInfo endpoint."],"exampleFix":"// before (custom grant saving only the access token)\nauthorization = OAuth2Authorization.from(existing).token(accessToken).build();\n\n// after (also persist the ID token so /userinfo can resolve it)\nauthorization = OAuth2Authorization.from(existing).token(accessToken).token(idToken).build();","handlingStrategy":"validation","validationCode":"boolean hasIdToken(OAuth2Authorization authorization) {\n    return authorization != null && authorization.getToken(OidcIdToken.class).isPresent();\n}","typeGuard":null,"tryCatchPattern":"try {\n    ResponseEntity<String> info = restTemplate.getForEntity(userInfoUrl, String.class);\n} catch (HttpStatusCodeException ex) {\n    if (ex.getResponseBodyAsString().contains(\"invalid_token\")) {\n        authorization = performFullAuthorizationCodeFlow(); // re-acquire token + ID token\n    }\n}","preventionTips":["Verify the token response contains an id_token before calling /userinfo.","Ensure custom OAuth2AuthorizationService implementations persist the OidcIdToken.","Re-authenticate when the ID token has expired rather than reusing the old access token."],"tags":["oauth2","oidc","id-token","user-info","spring-security"],"backgroundTag":"invalid-token","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}