apereo/cas · error · FailedLoginException

Unknown Duo Security authentication attempt

Error message

Unknown Duo Security authentication attempt

What it means

FailedLoginException thrown by DuoSecurityAuthenticationHandler.doAuthentication() when the presented credential is neither a DuoSecurityUniversalPromptCredential nor a DuoSecurityDirectCredential. The handler switches over the credential type and the default branch has no supported path, so it declares the authentication attempt unknown and fails it.

Solutions

  1. Ensure the flow creates and submits the correct credential: DuoSecurityUniversalPromptCredential for the Universal Prompt, or DuoSecurityDirectCredential (e.g. DuoSecurityPasscodeCredential) for direct/API mode.
  2. Review custom webflow actions/configurations that build credentials for the MFA hop and align them with the current CAS Duo credential classes.
  3. Check that only the intended Duo authentication handler handles this credential (authentication policy/selector misrouting another credential type into Duo).

Example fix

// before
val credential = new UsernamePasswordCredential(username, pass);
// after
val credential = new DuoSecurityDirectCredential(username, passcode);
Defensive patterns

Strategy: type-guard

Validate before calling

// Before invoking the handler, ensure credential type
if (!(credential instanceof DuoSecurityUniversalPromptCredential)
    && !(credential instanceof DuoSecurityDirectCredential)) {
    throw new IllegalArgumentException("Unsupported credential for Duo: " + credential.getClass());
}

Type guard

boolean isDuoCredential(Credential c) {
    return c instanceof DuoSecurityUniversalPromptCredential || c instanceof DuoSecurityDirectCredential;
}

Try / catch

try {
    result = duoHandler.authenticate(credential);
} catch (FailedLoginException e) {
    LOGGER.error("Duo credential type unsupported: {}", credential.getClass(), e);
}

Prevention

When it happens

Trigger: Invoking the Duo handler with any other Credential implementation — e.g. a plain UsernamePasswordCredential routed to the Duo MFA handler, a custom credential class, or a credential that lost its type after serialization/custom flow code.

Common situations: See trigger scenarios.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at support/cas-server-support-duo-core/src/main/java/org/apereo/cas/adaptors/duo/authn/DuoSecurityAuthenticationHandler.java:81

     * @return the result of this handler
     * @throws GeneralSecurityException general security exception for errors
     */
    @Override
    protected AuthenticationHandlerExecutionResult doAuthentication(final Credential credential, final Service service) throws Exception {
        return switch (credential) {
            case final DuoSecurityPasscodeCredential duo -> {
                LOGGER.debug("Attempting to authenticate credential via Duo Security passcode");
                yield authenticateDuoPasscodeCredential(duo);
            }
            case final DuoSecurityUniversalPromptCredential duo -> {
                LOGGER.debug("Attempting to authenticate credential via Duo Security universal prompt");
                yield authenticateDuoUniversalPromptCredential(duo);
            }
            case final DuoSecurityDirectCredential duo -> {
                LOGGER.debug("Attempting to directly authenticate credential against Duo");
                yield authenticateDuoApiCredential(duo);
            }
            default -> throw new FailedLoginException("Unknown Duo Security authentication attempt");
        };
    }

    /**
     * Resolve principal.
     *
     * @param principal the principal
     * @return the principal
     */
    protected Principal resolvePrincipal(final Principal principal) {
        return multifactorAuthenticationPrincipalResolver
            .stream()
            .filter(resolver -> resolver.supports(principal))
            .findFirst()
            .map(resolver -> resolver.resolve(principal))
            .orElseThrow(() -> new IllegalStateException("Unable to resolve principal for Duo Security multifactor authentication"));
    }

View on GitHub (pinned to e7288fc434)