{"record":{"id":"29ff992cc46a77e9","repo":"passbolt/passbolt_api","slug":"unable-to-verify-duo-authentication","errorCode":null,"errorMessage":"Unable to verify Duo authentication.","messagePattern":"Unable to verify Duo authentication\\.","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"plugins/PassboltCe/MultiFactorAuthentication/src/Service/Duo/MfaDuoEnableService.php","lineNumber":97,"sourceCode":"        MfaDuoCallbackDto $duoCallbackDto,\n        string $token\n    ): AuthenticationToken {\n        if (!Validation::uuid($token)) {\n            throw new InvalidArgumentException('The authentication token should be a valid UUID.');\n        }\n        $authenticationTokenType = AuthenticationToken::TYPE_MFA_SETUP;\n        $authenticationToken = (new MfaDuoCallbackAuthenticationTokenService())\n            ->consumeAndVerifyAuthenticationToken(\n                $uac,\n                $authenticationTokenType,\n                $token,\n                $duoCallbackDto->state\n            );\n        try {\n            (new MfaDuoVerifyDuoCodeService($authenticationTokenType, $this->duoClient))\n                ->verify($uac, $duoCallbackDto->duoCode);\n        } catch (Throwable $th) {\n            throw new BadRequestException(__('Unable to verify Duo authentication.'), null, $th);\n        }\n        $this->enableProvider($uac);\n\n        return $authenticationToken;\n    }\n\n    /**\n     * Enable the provider for the operator.\n     *\n     * @param \\App\\Utility\\UserAccessControl $uac The user access control\n     * @return void\n     * @throw InternalErrorException If it could not enable the Duo MFA provider.\n     */\n    private function enableProvider(UserAccessControl $uac): void\n    {\n        try {\n            MfaAccountSettings::enableProvider($uac, MfaSettings::PROVIDER_DUO);\n        } catch (Throwable $th) {","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltCe/MultiFactorAuthentication/src/Service/Duo/MfaDuoEnableService.php#L79-L115","documentation":"After consuming the callback token and checking the state, enable() delegates the user's Duo code verification to MfaDuoVerifyDuoCodeService, which exchanges the one-time Duo code with the Duo Universal SDK. Any Throwable in that verification (expired/already-used code, invalid code, network error to Duo API, client misconfiguration) is wrapped in this BadRequestException with the original error preserved as previous.","triggerScenarios":"Calling enable() with a duoCode that Duo rejects: code already redeemed by a prior request, code expired (Duo codes are short-lived), wrong Duo client credentials/api hostname, or unreachable Duo API from the server.","commonSituations":"Browser double-submits the callback and the second request reuses the consumed code; server clock skew or slow flow exceeding Duo's code TTL; Duo integration hostname/client id mismatch between org settings and the client used to start the prompt; firewall blocks outbound HTTPS to Duo.","solutions":["Restart the whole Duo flow — Duo codes are single-use and short-lived, so get a fresh prompt and code","Check getPrevious() to distinguish an invalid/expired code from a Duo API connectivity error","Verify the Duo org settings (client id, secret, api hostname) match the integration the user authenticated against","Ensure the server can reach https://<api-hostname> (outbound HTTPS, DNS, proxy) e.g. curl the Duo endpoint","Make the client submit the callback exactly once to avoid code replay"],"exampleFix":"// before\ntry {\n    $service->enable($uac, $dto, $token);\n} catch (BadRequestException $e) { /* swallowed */ }\n// after\ntry {\n    $service->enable($uac, $dto, $token);\n} catch (BadRequestException $e) {\n    $this->log('Duo verify failed: ' . $e->getPrevious()?->getMessage());\n    return $this->redirect('/mfa/setup/duo'); // restart flow for a fresh code\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    $service->enable($uac, $dto, $token);\n} catch (\\Cake\\Http\\Exception\\BadRequestException $e) {\n    $this->log('Duo code verification failed: ' . $e->getPrevious()?->getMessage());\n    // redirect to restart the Duo prompt for a fresh one-time code\n}","preventionTips":["Submit the Duo callback exactly once — codes are single-use","Check outbound connectivity to the Duo API hostname from the server","Keep org settings' client id/secret/hostname consistent between prompt start and verification","Treat any verification failure as flow-ending: restart with a new token and code"],"tags":["duo","mfa","verification-failed","bad-request","upstream"],"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"}