spring-projects/spring-security · error · BadCredentialsException
Empty basic authentication token
Error message
Empty basic authentication token
What it means
BasicAuthenticationConverter.convert parses the Authorization header and requires content after the 'Basic' scheme. If the header is exactly 'Basic' (case-insensitive, possibly with trailing whitespace trimmed) with no Base64 payload, it throws BadCredentialsException('Empty basic authentication token'). The scheme is present but the credentials are missing entirely.
Solutions
- Fix the client to send 'Basic ' + Base64(username:password)
- Verify proxies/gateways are not stripping the credential portion of the header
- Ensure client credentials (env vars, config) are actually populated and not empty strings
- Optionally pre-check the header client-side before sending
Example fix
// before
request.header("Authorization", "Basic"); // empty credentials
// after
String creds = Base64.getEncoder().encodeToString((user + ":" + pass).getBytes(UTF_8));
request.header("Authorization", "Basic " + creds); Defensive patterns
Strategy: try-catch
Validate before calling
String h = request.getHeader("Authorization");
if (h == null || h.trim().equalsIgnoreCase("Basic")) {
response.setHeader("WWW-Authenticate", "Basic realm=\"app\"");
response.sendError(401, "Missing basic credentials");
return;
} Type guard
boolean hasBasicCredentials(String header) {
return header != null && header.trim().toLowerCase().startsWith("basic ")
&& header.trim().length() > 6;
} Try / catch
try {
Authentication a = converter.convert(request);
} catch (BadCredentialsException e) {
response.setHeader("WWW-Authenticate", "Basic realm=\"app\"");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
} Prevention
- Always send 'Basic ' + Base64(username:password) with a space after the scheme
- Assert client credential config is non-empty at startup
- Check proxies/gateways don't strip the credential part
- Add contract tests asserting the exact Authorization header value
When it happens
Trigger: An Authorization: Basic header with no value is sent — e.g. a client sends the scheme but no credentials, a gateway strips the Base64 part, or curl/testing tools send an incomplete header.
Common situations: HTTP clients sending an empty credentials string; reverse proxies or API gateways rewriting Authorization headers; misconfigured clients where username/password variables are empty; manual curl testing with 'Authorization: Basic' only.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Invalid basic authentication token
- DigestAuthenticationFilter.missingMandatory
- invalid_request
- invalid_request
- invalid_token
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/1e49e070f636a3ad.
Report an issue: GitHub.
Appendix: source
Thrown at web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationConverter.java:89
public void setAuthenticationDetailsSource(
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
this.authenticationDetailsSource = authenticationDetailsSource;
}
@Override
public @Nullable UsernamePasswordAuthenticationToken convert(HttpServletRequest request) {
String header = request.getHeader(HttpHeaders.AUTHORIZATION);
if (header == null) {
return null;
}
header = header.trim();
if (!StringUtils.startsWithIgnoreCase(header, AUTHENTICATION_SCHEME_BASIC)) {
return null;
}
if (header.equalsIgnoreCase(AUTHENTICATION_SCHEME_BASIC)) {
throw new BadCredentialsException("Empty basic authentication token");
}
byte[] base64Token = header.substring(6).getBytes(StandardCharsets.UTF_8);
byte[] decoded = decode(base64Token);
String token = new String(decoded, getCredentialsCharset(request));
int delim = token.indexOf(":");
if (delim == -1) {
throw new BadCredentialsException("Invalid basic authentication token");
}
UsernamePasswordAuthenticationToken result = UsernamePasswordAuthenticationToken
.unauthenticated(token.substring(0, delim), token.substring(delim + 1));
result.setDetails(this.authenticationDetailsSource.buildDetails(request));
return result;
}
private byte[] decode(byte[] base64Token) {
try {
return Base64.getDecoder().decode(base64Token);
}View on GitHub (pinned to 96852e8860)