spring-projects/spring-security · error · BadCredentialsException

CasAuthenticationProvider.noServiceTicket

CasAuthenticationProvider.noServiceTicket

Error message

Failed to provide a CAS service ticket to validate

What it means

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.

Source

Thrown at cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java:118

	}

	@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());
		}
		if (result == null) {
			result = this.authenticateNow(authentication);
			result.setDetails(authentication.getDetails());
		}
		if (stateless) {
			// Add to cache
			this.statelessTicketCache.putTicketInCache(result);

View on GitHub (pinned to 96852e8860)

Solutions

  1. Verify the CAS service/callback URL matches the service registered with the CAS server so the ticket query parameter is preserved.
  2. Check that CasAuthenticationFilter is handling the callback (filterProcessesUrl) and receiving the ticket parameter.
  3. Redirect the user to the CAS login page to obtain a new ticket when none is present.
  4. If constructing tokens programmatically, set credentials to the actual service ticket string.
  5. Catch BadCredentialsException and restart the CAS authentication flow.

Example fix

// before
Authentication auth = new UsernamePasswordAuthenticationToken(principal, "");
provider.authenticate(auth); // throws
// after
String ticket = request.getParameter("ticket");
if (ticket != null && !ticket.isEmpty()) {
    Authentication auth = new CasServiceTicketAuthenticationToken(ticket, true);
    provider.authenticate(auth);
} else {
    response.sendRedirect(casLoginUrl); // obtain a new ticket
}
Defensive patterns

Strategy: validation

Validate before calling

Object creds = authentication.getCredentials();
if (creds == null || "".equals(creds)) {
    response.sendRedirect(casProperties.getLoginUrl()); // obtain a ticket first
    return;
}

Type guard

boolean hasServiceTicket(Authentication a) {
    Object c = a.getCredentials();
    return c instanceof String s && !s.isBlank();
}

Try / catch

try {
    return casAuthenticationProvider.authenticate(authentication);
} catch (BadCredentialsException e) {
    // no ticket present: redirect to CAS login
    return redirectService.initiateCasLogin();
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/d0e97ba627695e10. Report an issue: GitHub.