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

  1. Inspect the cause exception of the BadCredentialsException (ex.getCause()) to identify the real RuntimeException thrown by loadUserByUsername or credential verification.
  2. Verify the WebAuthn user exists in the credential/user records for the given username and that the UserDetailsService bean is correctly configured.
  3. 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

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


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)