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, the

View on GitHub (pinned to 96852e8860)

Solutions

  1. Always supply the service ticket as credentials when building the Authentication passed to CasAuthenticationProvider.
  2. If subclassing, keep the null-credential check before calling authenticateNow / TicketValidator.
  3. For custom Authentication types, override supports()/authenticate properly and map credentials into getCredentials().
  4. 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

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

Related errors


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