{"record":{"id":"d0e97ba627695e10","repo":"spring-projects/spring-security","slug":"casauthenticationprovider-noserviceticket","errorCode":"CasAuthenticationProvider.noServiceTicket","errorMessage":"Failed to provide a CAS service ticket to validate","messagePattern":"Failed to provide a CAS service ticket to validate","errorType":"exception","errorClass":"BadCredentialsException","httpStatus":null,"severity":"error","filePath":"cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java","lineNumber":118,"sourceCode":"\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());\n\t\t}\n\t\tif (result == null) {\n\t\t\tresult = this.authenticateNow(authentication);\n\t\t\tresult.setDetails(authentication.getDetails());\n\t\t}\n\t\tif (stateless) {\n\t\t\t// Add to cache\n\t\t\tthis.statelessTicketCache.putTicketInCache(result);","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java#L100-L136","documentation":"When authenticating a CasServiceTicketAuthenticationToken / UsernamePasswordAuthenticationToken, CasAuthenticationProvider requires non-empty credentials containing the CAS service ticket. If credentials are null or an empty string, it immediately throws BadCredentialsException — there is no ticket to hand to the TicketValidator, so CAS validation cannot proceed.","triggerScenarios":"authenticate() called with a token whose getCredentials() returns null or \"\" — e.g. POST to the CAS callback / j_spring_cas_security_check without a ticket parameter, a CAS server redirect lacking ?ticket=..., or code constructing an Authentication token manually without credentials.","commonSituations":"User bookmarking/reloading the callback URL (ticket already consumed and stripped); CAS server configured with a different service URL so the ticket parameter is dropped; proxy/gateway stripping query parameters; custom filter creating an empty token; CAS gateway mode where no ticket is issued.","solutions":["Verify the CAS service/callback URL matches the service registered with the CAS server so the ticket query parameter is preserved.","Check that CasAuthenticationFilter is handling the callback (filterProcessesUrl) and receiving the ticket parameter.","Redirect the user to the CAS login page to obtain a new ticket when none is present.","If constructing tokens programmatically, set credentials to the actual service ticket string.","Catch BadCredentialsException and restart the CAS authentication flow."],"exampleFix":"// before\nAuthentication auth = new UsernamePasswordAuthenticationToken(principal, \"\");\nprovider.authenticate(auth); // throws\n// after\nString ticket = request.getParameter(\"ticket\");\nif (ticket != null && !ticket.isEmpty()) {\n    Authentication auth = new CasServiceTicketAuthenticationToken(ticket, true);\n    provider.authenticate(auth);\n} else {\n    response.sendRedirect(casLoginUrl); // obtain a new ticket\n}","handlingStrategy":"validation","validationCode":"Object creds = authentication.getCredentials();\nif (creds == null || \"\".equals(creds)) {\n    response.sendRedirect(casProperties.getLoginUrl()); // obtain a ticket first\n    return;\n}","typeGuard":"boolean hasServiceTicket(Authentication a) {\n    Object c = a.getCredentials();\n    return c instanceof String s && !s.isBlank();\n}","tryCatchPattern":"try {\n    return casAuthenticationProvider.authenticate(authentication);\n} catch (BadCredentialsException e) {\n    // no ticket present: redirect to CAS login\n    return redirectService.initiateCasLogin();\n}","preventionTips":["Register the exact callback service URL with the CAS server so ?ticket= is delivered","Don't strip query parameters in proxies/gateways in front of the callback","Handle bookmarked callback URLs by redirecting to CAS login","In gateway/proxy (deferred) mode, expect empty tickets and fall back to redirect"],"tags":["spring-security","cas","bad-credentials","service-ticket"],"backgroundTag":"missing-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"}