spring-projects/spring-security · error · OAuth2AuthenticationException

invalid_token

invalid_token

Error message

Unable to authenticate the DPoP-bound access token.

What it means

DPoPAuthenticationProvider.authenticate() first authenticates the bearer access token with the delegate JwtAuthenticationProvider/OpaqueTokenAuthenticationProvider. If that delegate returns null (or a result that is not an AbstractOAuth2TokenAuthenticationToken), the provider cannot proceed to DPoP proof verification and throws OAuth2AuthenticationException with error code invalid_token and message 'Unable to authenticate the DPoP-bound access token.'

Source

Thrown at oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/DPoPAuthenticationProvider.java:84

	}

	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		DPoPAuthenticationToken dPoPAuthenticationToken = (DPoPAuthenticationToken) authentication;

		BearerTokenAuthenticationToken accessTokenAuthenticationRequest = new BearerTokenAuthenticationToken(
				dPoPAuthenticationToken.getAccessToken());
		Authentication accessTokenAuthenticationResult = this.tokenAuthenticationManager
			.authenticate(accessTokenAuthenticationRequest);

		AbstractOAuth2TokenAuthenticationToken<OAuth2Token> accessTokenAuthentication = null;
		if (accessTokenAuthenticationResult instanceof AbstractOAuth2TokenAuthenticationToken) {
			accessTokenAuthentication = (AbstractOAuth2TokenAuthenticationToken) accessTokenAuthenticationResult;
		}
		if (accessTokenAuthentication == null) {
			OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN,
					"Unable to authenticate the DPoP-bound access token.", null);
			throw new OAuth2AuthenticationException(error);
		}

		OAuth2AccessTokenClaims accessToken = new OAuth2AccessTokenClaims(accessTokenAuthentication.getToken(),
				accessTokenAuthentication.getTokenAttributes());

		DPoPProofContext dPoPProofContext = DPoPProofContext.withDPoPProof(dPoPAuthenticationToken.getDPoPProof())
			.accessToken(accessToken)
			.method(dPoPAuthenticationToken.getMethod())
			.targetUri(dPoPAuthenticationToken.getResourceUri())
			.build();
		JwtDecoder dPoPProofVerifier = this.dPoPProofVerifierFactory.createDecoder(dPoPProofContext);

		try {
			dPoPProofVerifier.decode(dPoPProofContext.getDPoPProof());
		}
		catch (Exception ex) {
			OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_DPOP_PROOF);
			throw new OAuth2AuthenticationException(error, ex);

View on GitHub (pinned to 96852e8860)

Solutions

  1. Fix the underlying access-token failure first (decode the token to see why it is rejected: signature, expiry, issuer, audience)
  2. Ensure the delegate (JwtAuthenticationProvider or OpaqueTokenAuthenticationProvider) is correctly configured with the right decoder/introspector
  3. Verify the client is sending a valid, unexpired DPoP-bound access token
  4. Check that DPoPAuthenticationProvider is registered after the token provider in the OAuth2ResourceServer configuration

Example fix

// before
http.oauth2ResourceServer(rs -> rs.jwt());
// after
http.oauth2ResourceServer(rs -> rs.jwt()
    .jwtAuthenticationProvider(new DPoPAuthenticationProvider(jwtAuthProvider, dPoPProofVerifier)));
Defensive patterns

Strategy: try-catch

Validate before calling

// resource server: confirm decoder config before wiring DPoP
// assert issuer/jwkSetUri reachable and algorithms match the AS

Try / catch

try {
    authenticationManager.authenticate(new BearerTokenAuthenticationToken(token));
} catch (OAuth2AuthenticationException e) {
    if ("invalid_token".equals(e.getError().getErrorCode())) {
        log.warn("DPoP-bound access token rejected: {}", e.getError().getDescription());
    }
    throw e;
}

Prevention

When it happens

Trigger: The DPoP-bound access token itself fails delegated authentication (invalid JWT, expired, wrong issuer/audience) so the delegate yields null; the DPoPAuthenticationProvider is wired with a delegate that cannot authenticate the presented token type.

Common situations: Resource server configured for DPoP but the JWT decoder rejects the token upstream; token expired or revoked; the delegate provider's decoder/introspector misconfigured for the issuer; clients sending a non-DPoP token to a DPoP-protected endpoint.

Understand the failure class

Related errors


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