apereo/cas · error · AuthenticationException

No multifactor authentication providers are available in…

Error message

No multifactor authentication providers are available in the application context. Authentication is blocked

What it means

MultifactorAuthenticationContingencyPlan.executeInternal() determines which MFA provider should handle a risky authentication. When no explicit provider is configured, it queries the context for available MFA providers; if the provider map is empty it logs this warning and throws AuthenticationException wrapping RiskyAuthenticationException, blocking the authentication outright. Risky logins cannot be remediated because no provider exists to step them up.

Solutions

  1. Add and configure at least one MFA provider (e.g. cas-server-support-otp-mfa or mfa-totp) and enable it via cas.authn.mfa.<provider>.* properties.
  2. Set cas.authn.adaptive.risk.response.mfa-provider to the id of an available provider so the plan does not depend on discovery.
  3. Confirm the MFA module's auto-configuration is active and its provider bean appears in the context (check ConditionalOnFeatureEnable).
  4. If MFA is not intended, adjust the risk response mode (e.g. block/audit) instead of relying on an MFA provider that does not exist.

Example fix

// before: risk response expects MFA but no provider configured
// (no cas.authn.mfa.* properties, no MFA module)
// after: configure a provider and point the risk response at it
cas.authn.mfa.totp.enabled=true
cas.authn.adaptive.risk.response.mfa-provider=mfa-totp
Defensive patterns

Strategy: validation

Validate before calling

val providers = MultifactorAuthenticationUtils
    .getAvailableMultifactorAuthenticationProviders(applicationContext);
if (casProperties.getAuthn().getAdaptive().getRisk().isEnabled()
    && providers.isEmpty()) {
    throw new IllegalStateException("Risk-based auth requires at least one MFA provider");
}

Try / catch

try {
    contingencyPlan.handle(riskyAuthEvent, authentication, service, request);
} catch (AuthenticationException e) {
    if (e.hasExceptionOfType(RiskyAuthenticationException.class)) {
        // degrade to block/deny flow or alert operator
        return blockedResponse(authentication);
    }
    throw e;
}

Prevention

When it happens

Trigger: A risky authentication is detected (cas.authn.adaptive.risk response triggered), cas.authn.adaptive.risk.response.mfaProvider is blank, and MultifactorAuthenticationUtils.getAvailableMultifactorAuthenticationProviders(applicationContext) returns an empty map — no MFA module (mfa-duo, mfa-totp, mfa-webauthn, etc.) is configured/enabled.

Common situations: Adaptive risk enabled in an overlay that has no MFA support module at all; MFA module present but its auto-configuration disabled or its provider bean conditionally skipped; typo'd/unsupported provider id in cas.authn.mfo.* leaving no provider registered.

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


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

Appendix: source

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

@Slf4j
public class MultifactorAuthenticationContingencyPlan extends BaseAuthenticationRiskContingencyPlan {

    public MultifactorAuthenticationContingencyPlan(final CasConfigurationProperties casProperties,
                                                    final ApplicationContext applicationContext) {
        super(casProperties, applicationContext);
    }

    @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);

View on GitHub (pinned to e7288fc434)