spring-projects/spring-security · error · BadCredentialsException
Authentication.getCredentials() cannot be null
Error message
Authentication.getCredentials() cannot be null
What it means
authenticateNow is the internal step that validates the service ticket against the CAS server. As a defensive invariant it re-checks that authentication.getCredentials() is non-null before calling TicketValidator.validate, throwing BadCredentialsException otherwise. (In practice the public authenticate() path already rejects empty credentials; this guards direct/internal invocation paths.)
Source
Thrown at cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java:145
// Try to obtain from cache
result = this.statelessTicketCache.getByTicketId(authentication.getCredentials().toString());
}
if (result == null) {
result = this.authenticateNow(authentication);
result.setDetails(authentication.getDetails());
}
if (stateless) {
// Add to cache
this.statelessTicketCache.putTicketInCache(result);
}
return result;
}
private CasAuthenticationToken authenticateNow(final Authentication authentication) throws AuthenticationException {
try {
Object credentials = authentication.getCredentials();
if (credentials == null) {
throw new BadCredentialsException("Authentication.getCredentials() cannot be null");
}
Assertion assertion = this.ticketValidator.validate(credentials.toString(), getServiceUrl(authentication));
UserDetails userDetails = loadUserByAssertion(assertion);
this.userDetailsChecker.check(userDetails);
Collection<GrantedAuthority> authorities = new ArrayList<>(
this.authoritiesMapper.mapAuthorities(userDetails.getAuthorities()));
authorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY));
return new CasAuthenticationToken(this.key, userDetails, credentials, authorities, userDetails, assertion);
}
catch (TicketValidationException ex) {
throw new BadCredentialsException(ex.getMessage(), ex);
}
}
/**
* Gets the serviceUrl. If the {@link Authentication#getDetails()} is an instance of
* {@link ServiceAuthenticationDetails}, then
* {@link ServiceAuthenticationDetails#getServiceUrl()} is used. Otherwise, theView on GitHub (pinned to 96852e8860)
Solutions
- Always supply the service ticket as credentials when building the Authentication passed to CasAuthenticationProvider.
- If subclassing, keep the null-credential check before calling authenticateNow / TicketValidator.
- For custom Authentication types, override supports()/authenticate properly and map credentials into getCredentials().
- Catch BadCredentialsException and reject/restart the authentication flow with proper credentials.
Example fix
// before
class MyToken extends AbstractAuthenticationToken {
public Object getCredentials() { return null; } // triggers error
}
// after
class MyToken extends AbstractAuthenticationToken {
private final String ticket;
public Object getCredentials() { return ticket; }
} Defensive patterns
Strategy: validation
Validate before calling
if (authentication.getCredentials() == null) {
throw new BadCredentialsException("Service ticket required");
} Type guard
boolean hasCredentials(Authentication a) { return a.getCredentials() != null; } Try / catch
try {
return casAuthenticationProvider.authenticate(authentication);
} catch (BadCredentialsException e) {
// credentials null: reject or re-prompt for ticket
} Prevention
- Always populate credentials when building Authentication tokens for CAS
- Don't override getCredentials() to return null in custom token classes
- When subclassing CasAuthenticationProvider, preserve the null-credential guard
When it happens
Trigger: authenticateNow invoked via authenticate() with a token whose credentials became null between the empty-string check and validation, or by subclass/legacy call paths that bypass the earlier check — e.g. a custom Authentication whose getCredentials() returns null.
Common situations: Custom Authentication implementations that don't carry credentials; subclassing CasAuthenticationProvider and invoking authenticateNow or overriding authenticate and dropping the credential check; tokens created from Authorization headers or principal-only data with credentials intentionally null.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- CasAuthenticationProvider.incorrectKey
- CasAuthenticationProvider.noServiceTicket
- RunAsImplAuthenticationProvider.incorrectKey
- <ticket validation failure message>
- Bad credentials
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/db5a3f4b782d2f7a.
Report an issue: GitHub.