spring-projects/spring-security · error · OAuth2AuthenticationException

invalid_token

invalid_token

Error message

Bearer token is malformed

What it means

The reactive (WebFlux) ServerBearerTokenAuthenticationConverter throws this when the Authorization header starts with 'Bearer ' but the remainder fails the token regex — typically whitespace or multiple values. It is the reactive counterpart of error 460 and maps to the invalid_token OAuth2 error before any token validation.

Source

Thrown at oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/authentication/ServerBearerTokenAuthenticationConverter.java:108

		if (!StringUtils.hasText(accessToken)) {
			BearerTokenError error = BearerTokenErrors
				.invalidRequest("The requested token parameter is an empty string");
			return Mono.error(new OAuth2AuthenticationException(error));
		}

		return Mono.just(accessToken);
	}

	private Mono<String> resolveFromAuthorizationHeader(HttpHeaders headers) {
		String authorization = headers.getFirst(this.bearerTokenHeaderName);
		if (!StringUtils.startsWithIgnoreCase(authorization, "bearer")) {
			return Mono.empty();
		}

		Matcher matcher = authorizationPattern.matcher(authorization);
		if (!matcher.matches()) {
			BearerTokenError error = BearerTokenErrors.invalidToken("Bearer token is malformed");
			throw new OAuth2AuthenticationException(error);
		}

		return Mono.just(matcher.group("token"));
	}

	private Flux<String> resolveAccessTokenFromQueryString(ServerHttpRequest request) {
		if (!this.allowUriQueryParameter || !HttpMethod.GET.equals(request.getMethod())) {
			return Flux.empty();
		}

		return resolveTokens(request.getQueryParams());
	}

	private Flux<String> resolveAccessTokenFromBody(ServerWebExchange exchange) {
		ServerHttpRequest request = exchange.getRequest();
		if (!this.allowFormEncodedBodyParameter
				|| !MediaType.APPLICATION_FORM_URLENCODED.equals(request.getHeaders().getContentType())
				|| !HttpMethod.POST.equals(request.getMethod())) {

View on GitHub (pinned to 96852e8860)

Solutions

  1. Trim the token and rebuild the header so it is exactly 'Bearer <token>'
  2. Regenerate the token if it contains characters outside the RFC token charset
  3. Inspect the raw header at the edge (e.g. with a WebFilter) to find what is actually transmitted
  4. Use a custom ServerBearerTokenAuthenticationConverter if your tokens legitimately deviate from the default pattern

Example fix

// before
webClient.defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token);
// after
webClient.defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token.trim());
Defensive patterns

Strategy: validation

Validate before calling

String auth = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
if (auth != null && auth.startsWith("Bearer ")) {
    String token = auth.substring(7).trim();
    if (token.isEmpty() || token.contains(" ")) {
        return Mono.error(new IllegalArgumentException("Malformed bearer token"));
    }
}

Type guard

boolean isValidBearerHeader(String header) {
    return header != null && header.matches("^Bearer [!-~]+$");
}

Try / catch

try {
    return chain.filter(exchange);
} catch (OAuth2AuthenticationException e) {
    if ("invalid_token".equals(e.getError().getErrorCode())) {
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        exchange.getResponse().getHeaders().add("WWW-Authenticate", "Bearer error=\"invalid_token\"");
    }
    return Mono.error(e);
}

Prevention

When it happens

Trigger: A WebFlux application receives 'Authorization: Bearer <value>' where <value> contains spaces, a newline, or multiple space-separated tokens, so authorizationPattern.matcher(authorization).matches() fails inside resolveFromAuthorizationHeader.

Common situations: Tokens copied with trailing whitespace from terminals or logs; clients URL-encoding or wrapping tokens; gateway header rewriting inserting spaces; empty bearer values ('Bearer ' with nothing after).

Understand the failure class

Related errors


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