spring-projects/spring-security · error · OAuth2AuthenticationException

invalid_token

invalid_token

Error message

invalid_token

What it means

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.

Source

Thrown at oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationProvider.java:112

		}

		if (this.logger.isTraceEnabled()) {
			this.logger.trace("Retrieved authorization with access token");
		}

		OAuth2Authorization.Token<OAuth2AccessToken> authorizedAccessToken = authorization.getAccessToken();
		Assert.notNull(authorizedAccessToken, "authorizedAccessToken cannot be null");
		if (!authorizedAccessToken.isActive()) {
			throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);
		}

		if (!authorizedAccessToken.getToken().getScopes().contains(OidcScopes.OPENID)) {
			throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);
		}

		OAuth2Authorization.Token<OidcIdToken> idToken = authorization.getToken(OidcIdToken.class);
		if (idToken == null) {
			throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);
		}

		if (this.logger.isTraceEnabled()) {
			this.logger.trace("Validated user info request");
		}

		OidcUserInfoAuthenticationContext authenticationContext = OidcUserInfoAuthenticationContext
			.with(userInfoAuthentication)
			.accessToken(authorizedAccessToken.getToken())
			.authorization(authorization)
			.build();
		OidcUserInfo userInfo = this.userInfoMapper.apply(authenticationContext);

		if (this.logger.isTraceEnabled()) {
			this.logger.trace("Authenticated user info request");
		}

		return new OidcUserInfoAuthenticationToken(accessTokenAuthentication, userInfo);

View on GitHub (pinned to 96852e8860)

Solutions

  1. Use an access token obtained from an authorization_code flow that issued an ID token (response includes id_token).
  2. Check that your OAuth2AuthorizationService implementation saves the OidcIdToken alongside the access token when the authorization completes.
  3. Re-run the full authorization flow to create a new OAuth2Authorization containing a current ID token.
  4. If you only need user data without OIDC semantics, expose a normal resource endpoint instead of the UserInfo endpoint.

Example fix

// before (custom grant saving only the access token)
authorization = OAuth2Authorization.from(existing).token(accessToken).build();

// after (also persist the ID token so /userinfo can resolve it)
authorization = OAuth2Authorization.from(existing).token(accessToken).token(idToken).build();
Defensive patterns

Strategy: validation

Validate before calling

boolean hasIdToken(OAuth2Authorization authorization) {
    return authorization != null && authorization.getToken(OidcIdToken.class).isPresent();
}

Try / catch

try {
    ResponseEntity<String> info = restTemplate.getForEntity(userInfoUrl, String.class);
} catch (HttpStatusCodeException ex) {
    if (ex.getResponseBodyAsString().contains("invalid_token")) {
        authorization = performFullAuthorizationCodeFlow(); // re-acquire token + ID token
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/78a5abee286f4ef7. Report an issue: GitHub.