spring-projects/spring-security · error · OAuth2AuthenticationException

invalid_token

invalid_token

Error message

Bearer token is malformed

What it means

This error is thrown by DefaultBearerTokenResolver when the Authorization header starts with 'Bearer ' but the remainder does not match the expected token pattern (a single non-whitespace token). The resolver treats any structurally invalid bearer credentials as an OAuth2AuthenticationException carrying the invalid_token error code, per RFC 6750. It signals that the client sent credentials the framework cannot even parse, before any token validation occurs.

Solutions

  1. Inspect the raw Authorization header and remove any whitespace/newlines inside or after the token value
  2. Ensure the client sends exactly 'Authorization: Bearer <single-token>' with no extra spaces
  3. Regenerate the token — some issuers emit tokens containing characters the resolver's pattern rejects
  4. If you need custom schemes, supply a custom BearerTokenResolver instead of the default

Example fix

// before
httpHeaders.set("Authorization", "Bearer " + token + " ");
// after
httpHeaders.set("Authorization", "Bearer " + token.trim());
Defensive patterns

Strategy: validation

Validate before calling

String auth = request.getHeader("Authorization");
if (auth != null && auth.startsWith("Bearer ")) {
    String token = auth.substring(7).trim();
    if (token.isEmpty() || token.matches(".*\\s.*")) {
        throw new IllegalArgumentException("Malformed bearer token");
    }
}

Type guard

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

Try / catch

try {
    chain.doFilter(request, response);
} catch (OAuth2AuthenticationException e) {
    if ("invalid_token".equals(e.getError().getErrorCode())) {
        response.setStatus(401);
        response.setHeader("WWW-Authenticate", "Bearer error=\"invalid_token\"");
    }
}

Prevention

When it happens

Trigger: Sending an Authorization header with a Bearer scheme whose value contains whitespace (e.g. a pasted token with a trailing newline or internal space), two tokens separated by a space, or an empty value after 'Bearer '. Also triggered by case/format variations that pass the prefix check but fail BearerTokenAuthenticationMatcher's regex, such as 'Bearer a b'.

Common situations: Tokens copied from logs or docs with wrapping whitespace; proxies or gateways mangling the header; misconfigured clients concatenating token type and value twice ('Bearer Bearer abc'); headers that include a newline from environment variable interpolation.

Understand the failure class

Related errors


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

Appendix: source

Thrown at oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/DefaultBearerTokenResolver.java:100

		if (accessToken != null && accessToken.isBlank()) {
			BearerTokenError error = BearerTokenErrors
				.invalidRequest("The requested token parameter is an empty string");
			throw new OAuth2AuthenticationException(error);
		}

		return accessToken;
	}

	private @Nullable String resolveFromAuthorizationHeader(HttpServletRequest request) {
		String authorization = request.getHeader(this.bearerTokenHeaderName);
		if (!StringUtils.startsWithIgnoreCase(authorization, "bearer")) {
			return null;
		}

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

		return matcher.group("token");
	}

	private @Nullable String resolveAccessTokenFromQueryString(HttpServletRequest request) {
		if (!this.allowUriQueryParameter || !HttpMethod.GET.name().equals(request.getMethod())) {
			return null;
		}

		return resolveToken(request.getParameterValues(ACCESS_TOKEN_PARAMETER_NAME));
	}

	private @Nullable String resolveAccessTokenFromBody(HttpServletRequest request) {
		if (!this.allowFormEncodedBodyParameter
				|| !MediaType.APPLICATION_FORM_URLENCODED_VALUE.equals(request.getContentType())
				|| HttpMethod.GET.name().equals(request.getMethod())) {
			return null;

View on GitHub (pinned to 96852e8860)