spring-projects/spring-security · error · OAuth2AuthenticationException

invalid_id_token

invalid_id_token

Error message

Missing (required) ID Token in Token Response for Client Registration: ${registrationId}

What it means

OpenID Connect requires the token response to include an id_token in additionalParameters. When the token endpoint's response lacks it, the OIDC provider cannot build an OidcUser, so invalid_id_token is thrown with the registration id in the message.

Source

Thrown at oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationProvider.java:158

			.getAuthorizationResponse();
		if (authorizationResponse.statusError()) {
			OAuth2Error error = authorizationResponse.getError();
			Assert.notNull(error, "error cannot be null when status is error");
			throw new OAuth2AuthenticationException(error, error.toString());
		}
		if (!Objects.equals(authorizationResponse.getState(), authorizationRequest.getState())) {
			OAuth2Error oauth2Error = new OAuth2Error(INVALID_STATE_PARAMETER_ERROR_CODE);
			throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
		}
		OAuth2AccessTokenResponse accessTokenResponse = getResponse(authorizationCodeAuthentication);
		ClientRegistration clientRegistration = authorizationCodeAuthentication.getClientRegistration();
		Map<String, Object> additionalParameters = accessTokenResponse.getAdditionalParameters();
		if (!additionalParameters.containsKey(OidcParameterNames.ID_TOKEN)) {
			OAuth2Error invalidIdTokenError = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE,
					"Missing (required) ID Token in Token Response for Client Registration: "
							+ clientRegistration.getRegistrationId(),
					null);
			throw new OAuth2AuthenticationException(invalidIdTokenError, invalidIdTokenError.toString());
		}
		OidcIdToken idToken = createOidcToken(clientRegistration, accessTokenResponse);
		validateNonce(authorizationRequest, idToken);
		OidcUser oidcUser = this.userService.loadUser(new OidcUserRequest(clientRegistration,
				accessTokenResponse.getAccessToken(), idToken, additionalParameters));
		Assert.notNull(oidcUser, "oidcUser cannot be null");
		Collection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper
			.mapAuthorities(oidcUser.getAuthorities());
		OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(
				authorizationCodeAuthentication.getClientRegistration(),
				authorizationCodeAuthentication.getAuthorizationExchange(), oidcUser, mappedAuthorities,
				accessTokenResponse.getAccessToken(), accessTokenResponse.getRefreshToken());
		authenticationResult.setDetails(authorizationCodeAuthentication.getDetails());
		return authenticationResult;
	}

	private OAuth2AccessTokenResponse getResponse(OAuth2LoginAuthenticationToken authorizationCodeAuthentication) {
		try {

View on GitHub (pinned to 96852e8860)

Solutions

  1. Ensure the openid scope is requested: .scope("openid", "profile", "email") in the client registration.
  2. Verify the tokenUri/issuer actually belongs to an OIDC provider that returns id_token for the code flow.
  3. Check that no custom token response client strips additionalParameters from the response.
  4. If the provider is OAuth2-only, drop OIDC login and use plain OAuth2Login without OidcUserService.

Example fix

// before
 registration.scope("read:user");
// after
 registration.scope("openid", "profile", "email");
Defensive patterns

Strategy: validation

Validate before calling

// assert OIDC readiness at startup
ClientRegistration reg = ...;
Set<String> scopes = reg.getScopes();
if (reg.getAuthorizationGrantType() == AuthorizationGrantType.AUTHORIZATION_CODE
        && !scopes.contains("openid")) {
    throw new IllegalStateException("OIDC login requires the 'openid' scope; registration "
        + reg.getRegistrationId() + " does not request it");
}

Try / catch

catch (OAuth2AuthenticationException ex) { if ("invalid_id_token".equals(ex.getError().getErrorCode())) { log.error("Provider did not return id_token; is this an OIDC provider and is 'openid' scoped?"); } throw ex; }

Prevention

When it happens

Trigger: Thrown in authenticate() when accessTokenResponse.getAdditionalParameters() does not contain OidcParameterNames.ID_TOKEN after a successful token exchange with an OIDC provider.

Common situations: Registration configured with scope=openid missing (so the OP issues a plain OAuth2 token, not an OIDC one), the OP not actually being an OIDC provider, a non-standard token endpoint that puts id_token elsewhere, or using authorization_code with a provider that only supports the implicit/hybrid flow.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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