spring-projects/spring-security · error · BadCredentialsException
<ex.getMessage()>
Error message
<ex.getMessage()>
What it means
WebAuthnAuthenticationProvider.authenticate() catches any RuntimeException from loading the user or building the WebAuthn authentication result and rethrows it as BadCredentialsException carrying the original message (the message here is ex.getMessage()). It deliberately masks internal exceptions as invalid credentials, so the actual cause is in the wrapped exception.
Solutions
- Inspect the cause exception of the BadCredentialsException (ex.getCause()) to identify the real RuntimeException thrown by loadUserByUsername or credential verification.
- Verify the WebAuthn user exists in the credential/user records for the given username and that the UserDetailsService bean is correctly configured.
- Fix the underlying data/infrastructure issue (missing user record, DB connectivity) rather than catching BadCredentialsException as a wrong-password signal.
Example fix
// before
try {
auth = provider.authenticate(token);
} catch (BadCredentialsException e) {
log.warn("bad credentials");
}
// after
try {
auth = provider.authenticate(token);
} catch (BadCredentialsException e) {
log.warn("bad credentials, cause=" + e.getCause(), e); // inspect real reason
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
// before authenticating, verify the user record exists UserDetails u = userDetailsService.loadUserByUsername(username); // throws UsernameNotFoundException if absent
Try / catch
try {
Authentication auth = authenticationManager.authenticate(token);
} catch (BadCredentialsException e) {
log.debug("WebAuthn auth failed: {}", e.getCause() != null ? e.getCause().toString() : e.getMessage());
throw new BadCredentialsException("authentication failed");
}
Prevention
- Always inspect getCause() — the provider masks the real RuntimeException in the message
- Keep user and credential records consistent (cascade deletes when removing users)
- Monitor for data-access exceptions appearing as BadCredentialsException — they indicate infra issues, not bad credentials
- Test the full flow with a deleted user to ensure graceful failure
When it happens
Trigger: The underlying UserDetailsService or WebAuthn entity lookup throws a RuntimeException during authentication — e.g. UsernameNotFoundException, a data-access failure, or a null/misconfigured userDetailsService bean — during a WebAuthn assertion attempt.
Common situations: Username encoded in userHandle not found in the database; user deleted between registration and login; database down; UserDetailsService miswired in the WebAuthnAuthenticationProvider configuration.
Understand the failure class
Background: "User not found", "Invalid user", and "does not exist": what missing-user lookup errors mean across Rocket.Chat, LiteLLM, Phabricator, rustfs, and pnpm — this error's family across 10 libraries.
Related errors
- Unable to authenticate the PublicKeyCredential
- Unable to authenticate the PublicKeyCredential. No…
- Credential with id already exists
- Unknown Callback
- Unused placeholders in template
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/69bf6b1c34969ccb.
Report an issue: GitHub.
Appendix: source
Thrown at webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationProvider.java:80
Assert.notNull(userDetailsService, "userDetailsService cannot be null");
this.relyingPartyOperations = relyingPartyOperations;
this.userDetailsService = userDetailsService;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
WebAuthnAuthenticationRequestToken webAuthnRequest = (WebAuthnAuthenticationRequestToken) authentication;
try {
PublicKeyCredentialUserEntity userEntity = this.relyingPartyOperations
.authenticate(webAuthnRequest.getWebAuthnRequest());
String username = userEntity.getName();
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
Collection<GrantedAuthority> authorities = new HashSet<>(userDetails.getAuthorities());
authorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY));
return new WebAuthnAuthentication(userEntity, authorities);
}
catch (RuntimeException ex) {
throw new BadCredentialsException(ex.getMessage(), ex);
}
}
@Override
public boolean supports(Class<?> authentication) {
return WebAuthnAuthenticationRequestToken.class.isAssignableFrom(authentication);
}
}
View on GitHub (pinned to 96852e8860)