spring-projects/spring-security · error · OAuth2AuthenticationException
server_error
server_error
Error message
The token generator failed to generate the access token.
What it means
In the token-exchange grant flow, after validating the request the provider asks the configured OAuth2TokenGenerator to produce an access token for the built OAuth2TokenContext. A null result means no registered generator (e.g. JWT encoder or opaque-token generator) supports the context, so the provider throws server_error with this message.
Source
Thrown at oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenExchangeAuthenticationProvider.java:245
.authorization(subjectAuthorization)
.principal(principal)
.authorizationServerContext(AuthorizationServerContextHolder.getContext())
.authorizedScopes(authorizedScopes)
.tokenType(OAuth2TokenType.ACCESS_TOKEN)
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.authorizationGrant(tokenExchangeAuthentication);
// @formatter:on
if (dPoPProof != null) {
tokenContextBuilder.put(OAuth2TokenContext.DPOP_PROOF_KEY, dPoPProof);
}
// ----- Access token -----
OAuth2TokenContext tokenContext = tokenContextBuilder.build();
OAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext);
if (generatedAccessToken == null) {
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
"The token generator failed to generate the access token.", ERROR_URI);
throw new OAuth2AuthenticationException(error);
}
if (this.logger.isTraceEnabled()) {
this.logger.trace("Generated access token");
}
// @formatter:off
OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(registeredClient)
.principalName(subjectAuthorization.getPrincipalName())
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.authorizedScopes(authorizedScopes)
.attribute(Principal.class.getName(), principal);
// @formatter:on
OAuth2AccessToken accessToken = OAuth2AuthenticationProviderUtils.accessToken(authorizationBuilder,
generatedAccessToken, tokenContext);
OAuth2Authorization authorization = authorizationBuilder.build();View on GitHub (pinned to 96852e8860)
Solutions
- Configure a token generator that supports the context: set oauth2AuthorizationServerConfigurer.tokenGenerator(...) with a JwtGenerator and/or OAuth2AccessTokenGenerator
- Register a NimbusJwtEncoder (JWKSource-based) bean so JwtGenerator can produce self-contained access tokens
- Check the registered authorization's token settings (accessTokenFormat) match an available generator (JWT vs opaque/REFERENCE_TOKEN)
- Enable trace logging to inspect the OAuth2TokenContext and confirm which generator is expected
Example fix
// before: no generator for JWT
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
// after
NimbusJwtEncoder jwtEncoder = new NimbusJwtEncoder(jwkSource);
JwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder);
jwtGenerator.setJwtCustomizer(...);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.tokenGenerator(jwtGenerator)
.tokenGenerator(new OAuth2AccessTokenGenerator()); Defensive patterns
Strategy: validation
Validate before calling
// startup check: ensure a generator exists for the configured token format
OAuth2TokenContext ctx = new OAuth2TokenContext() {} // build sample access-token context
if (tokenGenerator.generate(tokenContext) == null) {
throw new IllegalStateException("No OAuth2TokenGenerator configured for the access token format");
} Try / catch
try {
return tokenEndpoint.exchange(request);
} catch (OAuth2AuthenticationException e) {
if ("server_error".equals(e.getError().getErrorCode())) {
log.error("Token generation failed — check JwtEncoder/tokenGenerator configuration", e);
}
throw e;
} Prevention
- Always register JwtGenerator and/or OAuth2AccessTokenGenerator with OAuth2AuthorizationServerConfigurer.tokenGenerator(...)
- Provide a NimbusJwtEncoder backed by a JWKSource for self-contained tokens
- Match accessTokenFormat in token settings to an available generator
- Re-run authorization-server config tests after upgrading Spring Security versions
When it happens
Trigger: Calling the token endpoint with grant_type=token_exchange when the authorization server's OAuth2TokenGenerator cannot emit a token for the requested context — typically because neither a JwtGenerator nor an OAuth2AccessTokenGenerator matches the context (e.g. no JwtEncoder configured and token format not opaque-eligible).
Common situations: Security config missing a NimbusJwtEncoder bean on OAuth2AuthorizationServerConfigurer's tokenGenerator; token exchange enabled but the default token settings (e.g. reference vs self-contained) yield no matching generator; authorization-server metadata/customizer restricting the access-token format.
Related errors
- No enum constant org.springframework.security.oauth2.client.
- server_error
- server_error
- server_error
- The issuer identifier (${issuer}) cannot be set when isMulti
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/447879e3a5630e5e.
Report an issue: GitHub.