spring-projects/spring-security · error · OAuth2AuthenticationException

OAuth2Error from upstream authorization exception (dynamic)

Error message

OAuth2Error from upstream authorization exception (dynamic)

What it means

OAuth2LoginAuthenticationProvider.authenticate() delegates token exchange and user loading to downstream providers. When any of them throws an OAuth2AuthorizationException, it is re-wrapped as an OAuth2AuthenticationException carrying the upstream OAuth2Error (its toString() becomes the message). This is the normal failure funnel for Spring Security's OAuth2 login flow: the concrete cause is inside the wrapped error/exception.

Source

Thrown at oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProvider.java:122

		// REQUIRED. OpenID Connect requests MUST contain the "openid" scope value.
		if (loginAuthenticationToken.getAuthorizationExchange()
			.getAuthorizationRequest()
			.getScopes()
			.contains("openid")) {
			// This is an OpenID Connect Authentication Request so return null
			// and let OidcAuthorizationCodeAuthenticationProvider handle it instead
			return null;
		}
		OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthenticationToken;
		try {
			authorizationCodeAuthenticationToken = (OAuth2AuthorizationCodeAuthenticationToken) this.authorizationCodeAuthenticationProvider
				.authenticate(
						new OAuth2AuthorizationCodeAuthenticationToken(loginAuthenticationToken.getClientRegistration(),
								loginAuthenticationToken.getAuthorizationExchange()));
		}
		catch (OAuth2AuthorizationException ex) {
			OAuth2Error oauth2Error = ex.getError();
			throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);
		}
		OAuth2AccessToken accessToken = authorizationCodeAuthenticationToken.getAccessToken();
		Assert.notNull(accessToken, "accessToken cannot be null");
		Map<String, Object> additionalParameters = authorizationCodeAuthenticationToken.getAdditionalParameters();
		OAuth2User oauth2User = this.userService.loadUser(new OAuth2UserRequest(
				loginAuthenticationToken.getClientRegistration(), accessToken, additionalParameters));
		Assert.notNull(oauth2User, "oauth2User cannot be null");
		Collection<GrantedAuthority> authorities = new HashSet<>(oauth2User.getAuthorities());
		Collection<GrantedAuthority> mappedAuthorities = new LinkedHashSet<>(
				this.authoritiesMapper.mapAuthorities(authorities));
		mappedAuthorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY));
		OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(
				loginAuthenticationToken.getClientRegistration(), loginAuthenticationToken.getAuthorizationExchange(),
				oauth2User, mappedAuthorities, accessToken, authorizationCodeAuthenticationToken.getRefreshToken());
		authenticationResult.setDetails(loginAuthenticationToken.getDetails());
		return authenticationResult;
	}

View on GitHub (pinned to 96852e8860)

Solutions

  1. Inspect the wrapped OAuth2AuthenticationException's getError() (errorCode, description, uri) and cause to identify the real upstream failure.
  2. Verify the client registration (issuer URI, client-id/secret, redirect-uri, scopes) in your OAuth2Login configuration.
  3. Check that the authorization request completed in one session (state/cookies preserved) and the code is not replayed.
  4. If userinfo fails, hit the userinfo endpoint manually with the access token to see the raw error.

Example fix

// before: opaque 500 on login
// after: log the concrete OAuth2Error
try {
    authenticationManager.authenticate(token);
}
catch (OAuth2AuthenticationException ex) {
    logger.warn("OAuth2 login failed: code={}, desc={}", ex.getError().getErrorCode(), ex.getError().getDescription(), ex);
    throw ex;
}
Defensive patterns

Strategy: try-catch

Try / catch

try { auth = authenticationManager.authenticate(token); } catch (OAuth2AuthenticationException ex) { OAuth2Error err = ex.getError(); log.warn("login failed: {} - {}", err.getErrorCode(), err.getDescription(), ex); throw new LoginFailureException(err.getErrorCode(), ex); }

Prevention

When it happens

Trigger: Thrown when the inner OAuth2AuthorizationCodeAuthenticationProvider (invoked via AuthenticationManager.authenticate(new OAuth2AuthorizationCodeAuthenticationToken(...))) fails during the authorization-code token exchange, or when userService.loadUser() fails, and an OAuth2AuthorizationException propagates to this catch block.

Common situations: Authorization server rejects the code (expired/already-used code), token endpoint returns an error JSON, token response is malformed, client authentication fails (bad client_secret or JWK), or the user-info endpoint returns an error. Frequently hit during local dev when redirect URI/state handling is wrong.

Related errors


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