apereo/cas · error · AuthenticationException
Unable to extract credentials for multifactor authentication
Error message
Unable to extract credentials for multifactor authentication
What it means
During REST authentication, CAS resolved a multifactor trigger (a provider was selected by the MultifactorAuthenticationTriggerSelectionStrategy) but the credential factory could not extract the second-factor credentials from the request via fromAuthentication(...). Without MFA credentials the finalizeAuthenticationTransaction cannot proceed, so AuthenticationException is thrown.
Solutions
- Include the MFA provider's expected credential in the request body (e.g. otp / token / webauthn assertion parameter) alongside username/password.
- Confirm the MFA provider's REST credential extractor module is a dependency and registered with the REST credential factory.
- Check the multifactorTriggerSelectionStrategy configuration — if MFA should not be forced for this request, adjust the trigger strategy or service MFA policy.
- Verify the parameter names against the provider's documentation; provider versions rename fields.
- Test with debug logging on MultifactorAuthenticationTriggerSelectionStrategy and the credential factory to see which provider was selected and what it looked for.
Example fix
// before: MFA triggered but no OTP supplied curl -X POST https://cas/cas/v1/tickets -d 'username=u&password=p' // after: supply the second factor expected by the provider curl -X POST https://cas/cas/v1/tickets -d 'username=u&password=p&otp=123456'
Defensive patterns
Strategy: validation
Validate before calling
// ensure the MFA credential field the selected provider expects is present
if (body == null || !body.containsKey("otp")) { // adjust to provider field
// add second-factor credential before calling authenticate
} Try / catch
try {
Optional<AuthenticationResult> r = restAuthenticationService.authenticate(body, req, res);
} catch (AuthenticationException e) {
// prompt user for second factor and retry with MFA credential
} Prevention
- Know which MFA providers are forced for your services and what request fields they need.
- Ship the matching provider's REST support module so its extractor is registered.
- Include the second-factor value in every REST auth request when MFA is enforced.
- Align trigger strategy config with what REST clients can actually supply.
When it happens
Trigger: The request hits a service/flow that triggers an MFA provider (per service policy, principal attribute, request parameter like authnMethod/mfaProvider, etc.), yet the request body contains no credentials the provider's extractor recognizes — e.g. missing OTP/one-time-token field, wrong parameter name, or extractor module for that provider not registered in the REST credential factory.
Common situations: Client integrates the REST API and does not send the second-factor value (OTP) alongside the first-factor credentials; MFA provider forced globally by config but REST payloads never include OTP; provider changed (e.g. moving from webauthn to mfa-simple) so the expected request field changed; missing cas-server-support rest module for the specific MFA provider.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- No credentials can be extracted to authenticate the REST…
- No credentials are provided or extracted to authenticate…
- [ ] Caused by: [ ]
- Resolved credentials for this transaction are empty
- Unable to accept response status
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/b63779b83be3b599.
Report an issue: GitHub.
Appendix: source
Thrown at core/cas-server-core-rest-api/src/main/java/org/apereo/cas/rest/authentication/DefaultRestAuthenticationService.java:76
}
val service = serviceFactory.createService(request);
val registeredService = servicesManager.findServiceBy(service);
val authResult = Optional.ofNullable(
authenticationSystemSupport.handleInitialAuthenticationTransaction(service, credentials.toArray(Credential[]::new)));
return authResult
.map(result -> result.getInitialAuthentication()
.filter(Unchecked.predicate(authn -> restAuthenticationPolicy.isSatisfiedBy(authn, applicationContext).isSuccess()))
.filter(Unchecked.predicate(authn -> {
val validationResult = requestedContextValidator.validateAuthenticationContext(request, response, registeredService, authn, service);
return !validationResult.isSuccess();
}))
.map(Unchecked.function(authn -> multifactorTriggerSelectionStrategy.resolve(request, response, registeredService, authn, service)
.map(Unchecked.function(provider -> {
LOGGER.debug("Extracting credentials for multifactor authentication via [{}]", provider);
val authnCredentials = credentialFactory.fromAuthentication(request, requestBody, authn, provider);
if (authnCredentials == null || authnCredentials.isEmpty()) {
throw new AuthenticationException("Unable to extract credentials for multifactor authentication");
}
return authenticationSystemSupport.finalizeAuthenticationTransaction(service, authnCredentials);
}))
.orElseGet(Unchecked.supplier(() -> authenticationSystemSupport.finalizeAllAuthenticationTransactions(result, service)))))
.orElseGet(Unchecked.supplier(() -> authenticationSystemSupport.finalizeAllAuthenticationTransactions(result, service))));
}
}
View on GitHub (pinned to e7288fc434)