{"record":{"id":"cf1fa95514389436","repo":"passbolt/passbolt_api","slug":"unable-to-authenticate-to-duo-error-cf1fa9","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/DuoVerifyCallbackGetController.php","lineNumber":157,"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":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltCe/MultiFactorAuthentication/src/Controller/Duo/DuoVerifyCallbackGetController.php#L139-L175","documentation":"Thrown by DuoVerifyCallbackGetController::getAndAssertMfaDuoCallbackData when the Duo callback form executed but the resulting MfaDuoCallbackDto carries an error. The message is prefixed 'Unable to authenticate to Duo.' plus the formatted error from Duo's response (e.g. invalid code, access denied).","triggerScenarios":"GET /mfa/duo/verify/callback where Duo redirected back with an error parameter, the Duo authorization code is invalid/expired/reused, or the state request key does not match, causing MfaDuoCallbackForm->execute() to report an error in the DTO.","commonSituations":"User denies the Duo prompt; Duo authentication code expired due to slow redirect; clock skew between passbolt and Duo servers; wrong Duo client id/secret/host in passbolt MFA settings; callback replayed twice (code reuse).","solutions":["Read the appended formatted-error detail for the specific Duo failure reason and act on it.","Restart the MFA verification flow to obtain a fresh Duo authentication code.","Verify Duo provider settings (client id, client secret, API hostname) in the MFA organization settings.","Check server clock synchronization (NTP) — skewed time invalidates Duo tokens.","Ensure the callback is processed once; do not refresh/replay the callback URL after success."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// before processing callback, ensure Duo returned no error param\nconst params = new URLSearchParams(callbackUrl.split('?')[1]);\nif (params.has('error') || params.has('error_description')) { throw new Error('Duo returned an error: ' + params.get('error_description')); }","typeGuard":null,"tryCatchPattern":"try {\n  await duoVerifyCallback();\n} catch (e) {\n  if (e.status === 400 && /Unable to authenticate to Duo/.test(e.message)) {\n    restartDuoVerifyFlow(); // fresh auth code from a new Duo prompt\n  } else { throw e; }\n}","preventionTips":["Keep passbolt and Duo servers NTP-synchronized.","Complete the Duo redirect quickly so auth codes do not expire.","Never replay a consumed Duo auth code (no callback refresh).","Double-check Duo client id/secret/hostname in MFA settings."],"tags":["duo","mfa","authentication","callback"],"backgroundTag":"authentication-required","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"}