apereo/cas · error · MultifactorAuthenticationProviderAbsentException

Not all requested multifactor providers could be found…

Error message

Not all requested multifactor providers could be found. Requested providers are [<globalProviderIds>] and resolved providers are [<providerIds>]

What it means

GlobalMultifactorAuthenticationTrigger compares the globally requested MFA provider ids (cas.authn.mfa.global-provider-id, or from the principal attributes) against the providers actually resolved/registered in the CAS context. If any requested id has no matching MultifactorAuthenticationProvider bean, handleAbsentMultifactorProvider logs a warning and throws MultifactorAuthenticationProviderAbsentException, treating MFA as unsatisfiable.

Solutions

  1. Add/enable the CAS module that provides the missing provider (e.g. cas-server-support-gauth, duo, otp-mfa) so its auto-configuration registers the bean.
  2. Correct cas.authn.mfa.global-provider-id (and any principal-attribute provider ids) to match a registered provider id; check resolved ids in the WARN log.
  3. Verify the provider's feature/condition is enabled in cas.features (module present but inactive).
  4. If MFA should be optional, remove the global provider id instead of referencing a nonexistent provider.

Example fix

// before
cas.authn.mfa.global-provider-id=mfa-duo   // duo module not on classpath
// after: add dependency + matching config, or use an enabled provider
implementation "org.apereo.cas:cas-server-support-duo"
cas.authn.mfa.duo[0].registry-name=...
Defensive patterns

Strategy: validation

Validate before calling

val ids = casProperties.getAuthn().getMfa().getGlobalProviderId()
val available = providers.map { it.getId() }.toSet()
if (ids != null && !available.containsAll(Arrays.asList(ids.split(",")))) {
  throw new IllegalStateException("Unresolved MFA provider ids configured");
}

Try / catch

try { return trigger.isActivated(principal, service, request, context); }
catch (MultifactorAuthenticationProviderAbsentException e) {
  LOGGER.error("MFA provider missing", e);
  return Optional.empty();
}

Prevention

When it happens

Trigger: isActivated (or shouldMultifactorAuthenticatorsBeActivated) runs while cas.authn.mfa.global-provider-id (or the request/principal-supplied provider ids) references an id such as 'mfa-duo' for which no provider module/bean is registered.

Common situations: Setting global-provider-id but forgetting to include the corresponding provider dependency (e.g. cas-server-support-otp-mfa) and its configuration; typo in provider id (ids are like 'mfa-gauth', 'mfa-duo', 'mfa-simple'); provider bean filtered out by disabled feature conditions; copying config from another environment where the module is enabled.

Understand the failure class

Background: "X is required", "must be set", "cannot be empty": the missing-required-config error family, from Vertex AI project/location to WeChat keys — this error's family across 18 libraries.

Related errors


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

Appendix: source

Thrown at core/cas-server-core-authentication-mfa-api/src/main/java/org/apereo/cas/authentication/mfa/trigger/GlobalMultifactorAuthenticationTrigger.java:118

                return StringUtils.commaDelimitedListToSet(globalProviderId);
            })
            .filter(providers -> !providers.isEmpty())
            .orElseGet(() -> {
                val globalProviderId = casProperties.getAuthn().getMfa().getTriggers().getGlobal().getGlobalProviderId();
                return StringUtils.commaDelimitedListToSet(globalProviderId);
            });
    }

    protected void handleAbsentMultifactorProvider(final Set<String> globalProviderIds,
                                                   final List<MultifactorAuthenticationProvider> resolvedProviders) {
        val providerIds = resolvedProviders
            .stream()
            .map(MultifactorAuthenticationProvider::getId)
            .collect(Collectors.joining(","));
        val message = String.format("Not all requested multifactor providers could be found. "
            + "Requested providers are [%s] and resolved providers are [%s]", globalProviderIds, providerIds);
        LOGGER.warn(message, globalProviderIds);
        throw new MultifactorAuthenticationProviderAbsentException(message);
    }

    protected Optional<MultifactorAuthenticationProvider> resolveSingleMultifactorProvider(
        final MultifactorAuthenticationProvider resolvedProvider) {
        LOGGER.debug("Resolved single multifactor provider [{}]", resolvedProvider);
        return Optional.of(resolvedProvider);
    }

    protected Optional<MultifactorAuthenticationProvider> resolveMultifactorProvider(
        final Authentication authentication,
        final RegisteredService registeredService,
        final List<MultifactorAuthenticationProvider> resolvedProviders) throws Throwable {
        val principal = authentication.getPrincipal();
        val provider = multifactorAuthenticationProviderSelector.resolve(resolvedProviders, registeredService, principal);
        LOGGER.debug("Selected multifactor authentication provider for this transaction is [{}]", provider);
        return Optional.ofNullable(provider);
    }
}

View on GitHub (pinned to e7288fc434)