apereo/cas · error · AuthenticationException

No multifactor authentication providers are specified to…

Error message

No multifactor authentication providers are specified to handle risk-based authentication

What it means

When no explicit risk-response MFA provider is configured, executeInternal() discovers all available MFA providers. If exactly one is found it is used automatically; if more than one is available and none was specified, CAS cannot choose, logs this warning, and throws AuthenticationException wrapping MultifactorAuthenticationProviderAbsentException, blocking authentication. The deployer must explicitly select a provider when several exist.

Solutions

  1. Set cas.authn.adaptive.risk.response.mfa-provider to the id of the intended provider (e.g. mfa-duo).
  2. Verify the configured id matches a registered provider's id from MultifactorAuthenticationUtils.getAvailableMultifactorAuthenticationProviders.
  3. Disable or remove the unwanted MFA module so only one provider remains registered.
  4. Alternatively rely on registered-service MFA policies and configure the risk response to a definite provider to avoid ambiguity.

Example fix

// before: multiple providers, none selected for risk response
cas.authn.mfa.duo.enabled=true
cas.authn.mfa.totp.enabled=true
// after: explicitly select the provider for risky auth
cas.authn.mfa.duo.enabled=true
cas.authn.mfa.totp.enabled=true
cas.authn.adaptive.risk.response.mfa-provider=mfa-duo
Defensive patterns

Strategy: validation

Validate before calling

val providers = MultifactorAuthenticationUtils
    .getAvailableMultifactorAuthenticationProviders(applicationContext);
val configured = casProperties.getAuthn().getAdaptive().getRisk().getResponse().getMfaProvider();
if (providers.size() > 1 && StringUtils.isBlank(configured)) {
    throw new IllegalStateException(
        "Multiple MFA providers [" + providers.keySet() + "] available; set risk response mfa-provider");
}

Try / catch

try {
    contingencyPlan.handle(event, authentication, service, request);
} catch (AuthenticationException e) {
    if (e.hasExceptionOfType(MultifactorAuthenticationProviderAbsentException.class)) {
        LOGGER.error("Ambiguous risk-response MFA setup; no provider id configured");
        throw new HttpRequestException("MFA provider unresolvable", HttpStatus.SERVICE_UNAVAILABLE);
    }
    throw e;
}

Prevention

When it happens

Trigger: Risky authentication handled by MultifactorAuthenticationContingencyPlan while cas.authn.adaptive.risk.response.mfaProvider is blank and the context contains two or more registered MultifactorAuthenticationProvider beans (e.g. both Duo and TOTP configured).

Common situations: Overlay enables multiple MFA modules (common when mfa-duo and mfa-totp coexist) but the operator forgot to set the risk-response provider id; a newly added MFA module pushed the provider count from 1 to 2, breaking previously working implicit selection.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/e1af0861c89494d9. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-electrofence/src/main/java/org/apereo/cas/impl/plans/MultifactorAuthenticationContingencyPlan.java:51

    @Override
    protected AuthenticationRiskContingencyResponse executeInternal(final Authentication authentication,
                                                                    final RegisteredService service,
                                                                    final AuthenticationRiskScore score,
                                                                    final HttpServletRequest request) {
        var id = casProperties.getAuthn().getAdaptive().getRisk().getResponse().getMfaProvider();
        if (StringUtils.isBlank(id)) {
            LOGGER.debug("No explicit multifactor authentication provider is defined to handle risk-based authentication.");
            val providerMap = MultifactorAuthenticationUtils.getAvailableMultifactorAuthenticationProviders(this.applicationContext);
            if (providerMap.isEmpty()) {
                LOGGER.warn("No multifactor authentication providers are available in the application context. Authentication is blocked");
                throw new AuthenticationException(new RiskyAuthenticationException());
            }

            if (providerMap.size() == 1) {
                id = providerMap.values().iterator().next().getId();
            } else {
                LOGGER.warn("No multifactor authentication providers are specified to handle risk-based authentication");
                throw new AuthenticationException(new MultifactorAuthenticationProviderAbsentException());
            }
        }

        LOGGER.debug("Attempting to handle risk-based authentication via multifactor authentication provider [{}]", id);
        val attributeName = casProperties.getAuthn().getAdaptive().getRisk().getResponse().getRiskyAuthenticationAttribute();
        val newAuthn = DefaultAuthenticationBuilder.newInstance(authentication)
            .addAttribute(attributeName, Boolean.TRUE)
            .build();
        LOGGER.debug("Updated authentication to remember risk-based authentication via [{}]", attributeName);
        authentication.updateAttributes(newAuthn);
        return new AuthenticationRiskContingencyResponse(new Event(this, id));
    }
}

View on GitHub (pinned to e7288fc434)