spring-projects/spring-security · error · BadCredentialsException
CasAuthenticationProvider.incorrectKey
CasAuthenticationProvider.incorrectKey
Error message
The presented CasAuthenticationToken does not contain the expected key
What it means
CasAuthenticationProvider authenticates an already-established CasAuthenticationToken by comparing its stored keyHash with hashCode() of the provider's configured key. A mismatch means the token was produced by a different CAS provider/key (or tampered with), so BadCredentialsException is thrown. The key is a shared secret distinguishing which provider created stateless/stateful CAS tokens.
Source
Thrown at cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java:110
@Override
public void afterPropertiesSet() {
Assert.notNull(this.authenticationUserDetailsService, "An authenticationUserDetailsService must be set");
Assert.notNull(this.ticketValidator, "A ticketValidator must be set");
Assert.notNull(this.statelessTicketCache, "A statelessTicketCache must be set");
Assert.hasText(this.key,
"A Key is required so CasAuthenticationProvider can identify tokens it previously authenticated");
Assert.notNull(this.messages, "A message source must be set");
}
@Override
public @Nullable Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (!supports(authentication.getClass())) {
return null;
}
// If an existing CasAuthenticationToken, just check we created it
if (authentication instanceof CasAuthenticationToken) {
if (this.key.hashCode() != ((CasAuthenticationToken) authentication).getKeyHash()) {
throw new BadCredentialsException(this.messages.getMessage("CasAuthenticationProvider.incorrectKey",
"The presented CasAuthenticationToken does not contain the expected key"));
}
return authentication;
}
// Ensure credentials are presented
if ((authentication.getCredentials() == null) || "".equals(authentication.getCredentials())) {
throw new BadCredentialsException(this.messages.getMessage("CasAuthenticationProvider.noServiceTicket",
"Failed to provide a CAS service ticket to validate"));
}
boolean stateless = (authentication instanceof CasServiceTicketAuthenticationToken token
&& token.isStateless());
CasAuthenticationToken result = null;
if (stateless) {
// Try to obtain from cache
result = this.statelessTicketCache.getByTicketId(authentication.getCredentials().toString());View on GitHub (pinned to 96852e8860)
Solutions
- Ensure the provider key is identical everywhere it is configured (same CasAuthenticationProvider instance/key used to mint the token).
- Align keys across all nodes/environments (externalize to a shared property, e.g. cas.key).
- Re-authenticate from the original service ticket instead of re-passing an old CasAuthenticationToken.
- Catch BadCredentialsException for CAS tokens and force a fresh CAS login redirect.
Example fix
// before
CasAuthenticationProvider p1 = new CasAuthenticationProvider(); p1.setKey("ONE");
CasAuthenticationProvider p2 = new CasAuthenticationProvider(); p2.setKey("TWO"); // token minted by p1 fails here
// after
String sharedKey = env.getProperty("cas.provider-key");
p1.setKey(sharedKey); p2.setKey(sharedKey); Defensive patterns
Strategy: try-catch
Validate before calling
// ensure token key hash matches provider key before authenticating
if (token instanceof CasAuthenticationToken cat
&& cat.getKeyHash() != providerKey.hashCode()) {
// token came from a different provider — re-authenticate
} Type guard
null
Try / catch
try {
return casAuthenticationProvider.authenticate(authentication);
} catch (BadCredentialsException e) {
// key mismatch: restart CAS login flow
return redirectService.initiateCasLogin();
} Prevention
- Externalize the provider key and share it across nodes/environments
- Use one CasAuthenticationProvider instance per app
- Re-authenticate from a fresh service ticket instead of replaying old tokens
When it happens
Trigger: Passing a CasAuthenticationToken to authenticate() when the provider's <key> (constructor arg or CasAuthenticationProvider.setKey) differs from the key used when the token was created — e.g. two provider instances, key changed between deployments, or a token from another security filter chain.
Common situations: Multiple CAS providers configured with different keys in the same app; changing the key in config (or moving to environment-specific keys) invalidating previously issued tokens/tickets; load-balanced nodes with inconsistent key configuration; replaying a serialized token across apps.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- RunAsImplAuthenticationProvider.incorrectKey
- CasAuthenticationProvider.noServiceTicket
- Authentication.getCredentials() cannot be null
- <ticket validation failure message>
- Bad credentials
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/0da2f3791e3ea0f0.
Report an issue: GitHub.