spring-projects/spring-security · error · OAuth2AuthenticationException

server_error

server_error

Error message

The token generator failed to generate the access token.

What it means

When refreshing tokens (grant_type=refresh_token), the provider requests a new access token from the composed OAuth2TokenGenerator. If generate() returns null for the ACCESS_TOKEN context — no supporting generator — a server_error OAuth2AuthenticationException with 'The token generator failed to generate the access token.' is thrown.

Source

Thrown at oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProvider.java:212

				.authorizationServerContext(AuthorizationServerContextHolder.getContext())
				.authorization(authorization)
				.authorizedScopes(scopes)
				.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
				.authorizationGrant(refreshTokenAuthentication);
		// @formatter:on
		if (dPoPProof != null) {
			tokenContextBuilder.put(OAuth2TokenContext.DPOP_PROOF_KEY, dPoPProof);
		}

		OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.from(authorization);

		// ----- Access token -----
		OAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).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");
		}

		OAuth2AccessToken accessToken = OAuth2AuthenticationProviderUtils.accessToken(authorizationBuilder,
				generatedAccessToken, tokenContext);

		// ----- Refresh token -----
		OAuth2RefreshToken currentRefreshToken = refreshToken.getToken();
		if (!registeredClient.getTokenSettings().isReuseRefreshTokens()) {
			// @formatter:off
			tokenContext = tokenContextBuilder
					.tokenType(OAuth2TokenType.REFRESH_TOKEN)
					.authorization(authorizationBuilder.build())	// Refresh token generator/customizer may need access to the access token
					.build();
			// @formatter:on

View on GitHub (pinned to 96852e8860)

Solutions

  1. Use DelegatingOAuth2TokenGenerator(JwtGenerator, OAuth2AccessTokenGenerator, OAuth2RefreshTokenGenerator) to cover all formats.
  2. Match TokenSettings.getAccessTokenFormat() (SELF_CONTAINED/REFERENCE) of the RegisteredClient to the configured generators.
  3. Verify custom generators return non-null for ACCESS_TOKEN token-type contexts.
  4. Catch OAuth2AuthenticationException in the token endpoint and return the OAuth2Error (error=server_error) response.

Example fix

// before
.tokenGenerator(new OAuth2AccessTokenGenerator())  // client uses JWT
// after
.tokenGenerator(new DelegatingOAuth2TokenGenerator(
    new JwtGenerator(encoder), new OAuth2AccessTokenGenerator()));
Defensive patterns

Strategy: try-catch

Validate before calling

OAuth2TokenContext ctx = new OAuth2TokenContextBuilder()
    .tokenType(OAuth2TokenType.ACCESS_TOKEN)
    .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
    .registeredClient(registeredClient).build();
if (tokenGenerator.generate(ctx) == null) {
    throw new IllegalStateException("No generator produces access tokens for client " + registeredClient.getId());
}

Try / catch

try {
    Authentication result = provider.authenticate(refreshRequest);
} catch (OAuth2AuthenticationException e) {
    if (OAuth2ErrorCodes.SERVER_ERROR.equals(e.getError().getErrorCode())) {
        // inspect tokenGenerator configuration before retrying
        throw new IllegalStateException("Access token generator unsupported for this client", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: OAuth2RefreshTokenAuthenticationProvider.authenticate() validating the refresh token and then generating: tokenGenerator.generate(access-token context) == null, typically because the configured generators don't match the client's access token format settings.

Common situations: Authorization server customized with tokenGenerator(new JwtGenerator(...)) only while the client's TokenSettings demand REFERENCE (opaque) tokens; vice versa with OAuth2AccessTokenGenerator only and SELF_CONTAINED required; upgrading Spring Authorization Server and dropping part of the default generator chain.

Related errors


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