{"record":{"id":"032542ff84fe6562","repo":"apereo/cas","slug":"duo-security-universal-prompt-authentication-has-f","errorCode":null,"errorMessage":"Duo Security universal prompt authentication has failed","messagePattern":"Duo Security universal prompt authentication has failed","errorType":"exception","errorClass":"FailedLoginException","httpStatus":null,"severity":"error","filePath":"support/cas-server-support-duo-core/src/main/java/org/apereo/cas/adaptors/duo/authn/DuoSecurityAuthenticationHandler.java","lineNumber":127,"sourceCode":"            LoggingUtils.error(LOGGER, e);\n        }\n        throw new FailedLoginException(\"Duo Security passcode authentication has failed\");\n    }\n\n    private AuthenticationHandlerExecutionResult authenticateDuoUniversalPromptCredential(\n        final DuoSecurityUniversalPromptCredential credential) throws Exception {\n        try {\n            val duoAuthenticationService = multifactorAuthenticationProvider.getObject().getDuoAuthenticationService();\n            val result = duoAuthenticationService.authenticate(credential);\n            if (result.isSuccess()) {\n                val principal = principalFactory.createPrincipal(result.getUsername(), result.getAttributes());\n                LOGGER.debug(\"Duo Security Universal Prompt has successfully authenticated [{}]\", Objects.requireNonNull(principal).getId());\n                return createHandlerResult(credential, principal, new ArrayList<>());\n            }\n        } catch (final Throwable e) {\n            LoggingUtils.error(LOGGER, e);\n        }\n        throw new FailedLoginException(\"Duo Security universal prompt authentication has failed\");\n    }\n\n    private AuthenticationHandlerExecutionResult authenticateDuoApiCredential(\n        final DuoSecurityDirectCredential credential) throws FailedLoginException {\n        try {\n            val duoAuthenticationService = multifactorAuthenticationProvider.getObject().getDuoAuthenticationService();\n            if (duoAuthenticationService.authenticate(credential).isSuccess()) {\n                val principal = resolvePrincipal(credential.getPrincipal());\n                LOGGER.debug(\"Duo Security has successfully authenticated [{}]\", principal.getId());\n                return createHandlerResult(credential, principal, new ArrayList<>());\n            }\n        } catch (final Exception e) {\n            LoggingUtils.error(LOGGER, e);\n        }\n        throw new FailedLoginException(\"Duo Security authentication has failed\");\n    }\n}\n","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-duo-core/src/main/java/org/apereo/cas/adaptors/duo/authn/DuoSecurityAuthenticationHandler.java#L109-L145","documentation":"FailedLoginException thrown by authenticateDuoUniversalPromptCredential() when authentication via the Duo Universal Prompt (secondary authentication call with a signed Duo response) fails or throws. Like the passcode path, all Throwables are logged via LoggingUtils.error and rethrown as this generic FailedLoginException.","triggerScenarios":"doAuthentication with a DuoSecurityUniversalPromptCredential whose Duo-issued signed response cannot be verified or whose authentication result is not approved — expired/failed Duo transaction, user denied the push, state parameter mismatch, or any exception calling the Duo service.","commonSituations":"User denied or ignored the Duo push; Duo transaction timed out before approval; misconfigured redirect URI / client id / client secret for Universal Prompt (OIDC-based) causing response validation failure; CAS restart losing in-flight Duo transaction state; clock skew affecting token validation.","solutions":["Check the stack trace logged via LoggingUtils.error preceding this exception to distinguish user-denied vs configuration errors.","Have the user retry and approve the prompt within the timeout window.","Verify the Universal Prompt client configuration (client id, client secret, API host) and the CAS callback/redirect URL match the Duo protected application.","Confirm the Duo application in the Duo Admin console uses the Universal Prompt and is in an active, approved state."],"exampleFix":"// before: mismatched Duo OIDC redirect\ncas.authn.mfa.duo[0].redirect-uri=https://cas.example.org/cas/wrong/callback\n// after\ncas.authn.mfa.duo[0].redirect-uri=https://cas.example.org/cas/login?client_id=... (registered Duo redirect)","handlingStrategy":"try-catch","validationCode":"// Pre-validate Universal Prompt config at startup\nclass DuoPromptPrecheck {\n    void check(DuoSecurityDuoAdminProperties p) {\n        if (p.getDuoApiHost() == null || p.getClientId() == null || p.getClientSecret() == null)\n            throw new IllegalStateException(\"Universal Prompt client config incomplete\");\n    }\n}","typeGuard":"boolean isUniversalPromptCredential(Credential c) {\n    return c instanceof DuoSecurityUniversalPromptCredential;\n}","tryCatchPattern":"try {\n    result = duoHandler.authenticate(promptCredential);\n} catch (FailedLoginException e) {\n    // differentiate: user-denied (retry) vs config error (alert admin)\n    LOGGER.error(\"Universal Prompt auth failed; see prior logged cause\", e);\n}","preventionTips":["Check the LoggingUtils.error stack trace to separate user-denied results from configuration failures.","Register the exact CAS redirect URI in the Duo Universal Prompt application.","Keep Duo transaction timeouts in mind; instruct users to approve promptly.","Avoid CAS restarts mid-flow for stateless verification of in-flight Duo transactions."],"tags":["duo","mfa","universal-prompt","failed-login"],"backgroundTag":"authentication-required","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}