{"record":{"id":"0da2f3791e3ea0f0","repo":"spring-projects/spring-security","slug":"casauthenticationprovider-incorrectkey","errorCode":"CasAuthenticationProvider.incorrectKey","errorMessage":"The presented CasAuthenticationToken does not contain the expected key","messagePattern":"The presented CasAuthenticationToken does not contain the expected key","errorType":"exception","errorClass":"BadCredentialsException","httpStatus":null,"severity":"error","filePath":"cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java","lineNumber":110,"sourceCode":"\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notNull(this.authenticationUserDetailsService, \"An authenticationUserDetailsService must be set\");\n\t\tAssert.notNull(this.ticketValidator, \"A ticketValidator must be set\");\n\t\tAssert.notNull(this.statelessTicketCache, \"A statelessTicketCache must be set\");\n\t\tAssert.hasText(this.key,\n\t\t\t\t\"A Key is required so CasAuthenticationProvider can identify tokens it previously authenticated\");\n\t\tAssert.notNull(this.messages, \"A message source must be set\");\n\t}\n\n\t@Override\n\tpublic @Nullable Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tif (!supports(authentication.getClass())) {\n\t\t\treturn null;\n\t\t}\n\t\t// If an existing CasAuthenticationToken, just check we created it\n\t\tif (authentication instanceof CasAuthenticationToken) {\n\t\t\tif (this.key.hashCode() != ((CasAuthenticationToken) authentication).getKeyHash()) {\n\t\t\t\tthrow new BadCredentialsException(this.messages.getMessage(\"CasAuthenticationProvider.incorrectKey\",\n\t\t\t\t\t\t\"The presented CasAuthenticationToken does not contain the expected key\"));\n\t\t\t}\n\t\t\treturn authentication;\n\t\t}\n\n\t\t// Ensure credentials are presented\n\t\tif ((authentication.getCredentials() == null) || \"\".equals(authentication.getCredentials())) {\n\t\t\tthrow new BadCredentialsException(this.messages.getMessage(\"CasAuthenticationProvider.noServiceTicket\",\n\t\t\t\t\t\"Failed to provide a CAS service ticket to validate\"));\n\t\t}\n\n\t\tboolean stateless = (authentication instanceof CasServiceTicketAuthenticationToken token\n\t\t\t\t&& token.isStateless());\n\t\tCasAuthenticationToken result = null;\n\n\t\tif (stateless) {\n\t\t\t// Try to obtain from cache\n\t\t\tresult = this.statelessTicketCache.getByTicketId(authentication.getCredentials().toString());","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java#L92-L128","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nCasAuthenticationProvider p1 = new CasAuthenticationProvider(); p1.setKey(\"ONE\");\nCasAuthenticationProvider p2 = new CasAuthenticationProvider(); p2.setKey(\"TWO\"); // token minted by p1 fails here\n// after\nString sharedKey = env.getProperty(\"cas.provider-key\");\np1.setKey(sharedKey); p2.setKey(sharedKey);","handlingStrategy":"try-catch","validationCode":"// ensure token key hash matches provider key before authenticating\nif (token instanceof CasAuthenticationToken cat\n        && cat.getKeyHash() != providerKey.hashCode()) {\n    // token came from a different provider — re-authenticate\n}","typeGuard":"null","tryCatchPattern":"try {\n    return casAuthenticationProvider.authenticate(authentication);\n} catch (BadCredentialsException e) {\n    // key mismatch: restart CAS login flow\n    return redirectService.initiateCasLogin();\n}","preventionTips":["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"],"tags":["spring-security","cas","bad-credentials","authentication"],"backgroundTag":"invalid-credentials","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}