{"record":{"id":"e2631c5b9dcf40ad","repo":"passbolt/passbolt_api","slug":"unable-to-authenticate-to-duo-error","errorCode":null,"errorMessage":"Unable to authenticate to Duo. {error}","messagePattern":"Unable to authenticate to Duo\\. (.+?)","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"plugins/PassboltCe/MultiFactorAuthentication/src/Controller/Duo/DuoSetupCallbackGetController.php","lineNumber":165,"sourceCode":"\n    /**\n     * Get the Mfa Duo Callback data from the query and assert them.\n     *\n     * @throws \\App\\Error\\Exception\\FormValidationException If the data provided on the query does not validate\n     * @throws \\Cake\\Http\\Exception\\BadRequestException If Duo was not able to authenticate the user and provided error details\n     * @return \\Passbolt\\MultiFactorAuthentication\\Model\\Dto\\MfaDuoCallbackDto\n     */\n    private function getAndAssertMfaDuoCallbackData(): MfaDuoCallbackDto\n    {\n        $mfaDuoCallbackData = $this->getRequest()->getQueryParams();\n        $mfaDuoCallbackForm = new DuoCallbackForm();\n        $isValid = $mfaDuoCallbackForm->execute($mfaDuoCallbackData);\n        $mfaDuoCallbackDto = new MfaDuoCallbackDto($mfaDuoCallbackForm->getData());\n\n        if ($mfaDuoCallbackDto->hasError()) {\n            $msg = __('Unable to authenticate to Duo.');\n            $msg .= \" {$mfaDuoCallbackDto->formatError()}\";\n            throw new BadRequestException($msg);\n        }\n\n        if (!$isValid) {\n            $msg = __('Unable to validate the Duo callback data.');\n            throw new FormValidationException($msg, $mfaDuoCallbackForm);\n        }\n\n        return $mfaDuoCallbackDto;\n    }\n\n    /**\n     * Consume the duo state cookie containing the user authentication token id and assert the format this one.\n     *\n     * @return string The token id stored in the cookie\n     * @throws \\Cake\\Http\\Exception\\BadRequestException if the cookie is not defined\n     * @throws \\Cake\\Http\\Exception\\BadRequestException if the cookie value is not a string\n     * @throws \\Cake\\Http\\Exception\\BadRequestException if the cookie value is not a valid uuid\n     */","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltCe/MultiFactorAuthentication/src/Controller/Duo/DuoSetupCallbackGetController.php#L147-L183","documentation":"Thrown by DuoSetupCallbackGetController::getAndAssertMfaDuoCallbackData when the Duo callback DTO reports an error after executing MfaDuoCallbackForm — passbolt could not authenticate the OAuth-style callback from Duo during MFA setup. The Duo error from the callback response is appended to the message.","triggerScenarios":"Duo redirects back to /mfa/verify Duo setup callback with an error parameter (user denied the prompt, invalid state nonce, wrong Duo client id/secret, or expired request) and the form execution yields a DTO error.","commonSituations":"Mismatched Duo application credentials (client secret / integration key) between Duo admin console and passbolt config; clock skew invalidating signed responses; user clicking 'Deny' in Duo prompt; reverse proxy stripping query parameters from the callback URL.","solutions":["Read the appended {error} detail and fix the root cause it reports (e.g. invalid credentials or denied prompt)","Verify Duo client id, client secret, and API hostname in passbolt MFA settings match the Duo application","Check server clock synchronization (NTP) since Duo signed responses are time-sensitive","Ensure the callback URL configured in Duo matches the passbolt endpoint exactly"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Before reaching the callback, confirm Duo config matches the Duo application\nconst params = new URLSearchParams(window.location.search);\nif (!params.get('code') || !params.get('state')) {\n  // malformed callback: restart setup instead of forwarding\n}","typeGuard":null,"tryCatchPattern":"try {\n  await mfaService.completeDuoSetup(callbackParams);\n} catch (e) {\n  if (e instanceof BadRequestException && /Unable to authenticate to Duo/.test(e.message)) {\n    // inspect e.message detail; typically restart setup after fixing credentials\n  }\n}","preventionTips":["Keep Duo client id/secret/API hostname in sync with the Duo admin console","Keep server clocks NTP-synchronized","Never modify or trim the redirect URL Duo provides"],"tags":["mfa","duo","callback","authentication"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}