apereo/cas · error · FailedLoginException
Unable to validate multifactor credential with status
Error message
Unable to validate multifactor credential with status
What it means
RestfulCasSimpleMultifactorAuthenticationService.generate calls an external REST endpoint to validate the user's MFA credential and mint a simple-MFA token. If the endpoint responds with an unexpected/non-success statusCode (not the accepted success status with a token payload), it throws FailedLoginException "Unable to validate multifactor credential with status <statusCode>". The MFA decision was delegated to the REST service and it refused or errored.
Solutions
- Check the remote MFA REST service's response status and logs for why validation failed
- Verify cas.authn.mfa.rest (simple-mfa REST) endpoint URL, method, and auth credentials are correct
- Confirm the remote service returns the expected body/token format on success
- Retry after fixing the upstream service; treat 5xx as transient and re-attempt the MFA step
Defensive patterns
Strategy: retry
Validate before calling
// preflight the REST MFA endpoint availability and expected contract
var probe = HttpUtils.executeHttpRequestToServices(...mfaRestUrl, HttpMethod.GET, ...);
if (probe.getStatus() != 200) throw new IllegalStateException("MFA REST endpoint unhealthy: " + probe.getStatus()); Try / catch
try {
return restMfaService.generate(principal, service, credential);
} catch (FailedLoginException e) {
if (isServerError(statusCode)) { // 5xx: transient upstream issue
return retryWithBackoff(() -> restMfaService.generate(principal, service, credential));
}
throw e; // 4xx: user's code genuinely rejected; prompt for a new code
} Prevention
- Health-check the MFA REST endpoint in deployment pipelines
- Verify endpoint URL, HTTP method, and auth headers in cas.authn.mfa.rest configuration
- Match the exact response status/body the remote service returns on success
- Distinguish 4xx (bad code, prompt user) from 5xx (upstream issue, retry) in handling
When it happens
Trigger: generate() when the HTTP response from the configured simple-mfa REST endpoint returns a status code outside the expected success range, or a success status without the expected token body — e.g. 401/403 wrong code, 404/500 endpoint problems.
Common situations: Remote MFA service rejecting the submitted code (wrong/expired code); wrong REST endpoint URL or credentials configured for the MFA REST service; the remote service returning an undocumented status or empty body on success; network/proxy layer returning 502/503.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- Unable to extract credentials for multifactor authentication
- Unable to accept response status
- <policy status exception>
- State [ : : ] does not have a matching transition for
- Not all requested multifactor providers could be found…
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/edd587f3fd42ca3c.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-simple-mfa-core/src/main/java/org/apereo/cas/mfa/simple/validation/RestfulCasSimpleMultifactorAuthenticationService.java:90
.entity(writer.toString())
.basicAuthPassword(properties.getBasicAuthPassword())
.basicAuthUsername(properties.getBasicAuthUsername())
.maximumRetryAttempts(properties.getMaximumRetryAttempts())
.parameters(parameters)
.build();
response = HttpUtils.execute(exec);
val statusCode = response.getCode();
if (HttpStatus.valueOf(statusCode).is2xxSuccessful()) {
try (val content = ((HttpEntityContainer) response).getEntity().getContent()) {
val result = IOUtils.toString(content, StandardCharsets.UTF_8);
val mfaFactory = (CasSimpleMultifactorAuthenticationTicketFactory) ticketFactory.get(CasSimpleMultifactorAuthenticationTicket.class);
LOGGER.debug("Multifactor authentication token received is [{}]", result);
val token = mfaFactory.create(result, service, CollectionUtils.wrap(CasSimpleMultifactorAuthenticationConstants.PROPERTY_PRINCIPAL, principal));
LOGGER.debug("Created multifactor authentication token [{}] for service [{}]", token.getId(), service);
return token;
}
}
throw new FailedLoginException("Unable to validate multifactor credential with status " + statusCode);
} finally {
HttpUtils.close(response);
}
}
@Override
public void store(final CasSimpleMultifactorAuthenticationTicket token) throws Exception {
HttpResponse response = null;
try (val writer = new StringWriter()) {
MAPPER.writer().with(new MinimalPrettyPrinter()).writeValue(writer, token);
val headers = CollectionUtils.<String, String>wrap(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
headers.putAll(properties.getHeaders());
val exec = HttpExecutionRequest.builder()
.method(HttpMethod.POST)
.headers(headers)
.url(properties.getUrl())
.entity(writer.toString())View on GitHub (pinned to e7288fc434)