{"record":{"id":"eba3afeb23071218","repo":"passbolt/passbolt_api","slug":"could-not-validate-the-duo-settings","errorCode":null,"errorMessage":"Could not validate the Duo settings.","messagePattern":"Could not validate the Duo settings\\.","errorType":"http","errorClass":"InternalErrorException","httpStatus":500,"severity":"error","filePath":"plugins/PassboltCe/MultiFactorAuthentication/src/Service/Duo/MfaDuoGetSdkClientService.php","lineNumber":53,"sourceCode":"     * Get the Duo Sdk Client object or fail.\n     *\n     * @param \\Passbolt\\MultiFactorAuthentication\\Service\\MfaOrgSettings\\MfaOrgSettingsDuoService $settings Duo org settings\n     * @param string $tokenType Authentication token type -- used to know whether the callback is for the setup or verify flow\n     * @return \\Duo\\DuoUniversal\\Client\n     * @throws \\Cake\\Http\\Exception\\InternalErrorException If it cannot instantiate the Duo Sdk client.\n     */\n    public function getOrFail(MfaOrgSettingsDuoService $settings, string $tokenType): Client\n    {\n        try {\n            return new Client(\n                $settings->getDuoClientId(),\n                $settings->getDuoClientSecret(),\n                $settings->getDuoApiHostname(),\n                $this->getCallbackRedirectUrl($tokenType),\n                true,\n            );\n        } catch (DuoException $e) {\n            throw new InternalErrorException(__('Could not validate the Duo settings.'), null, $e);\n        }\n    }\n\n    /**\n     * Get the callback redirect URL to redirect the user from Duo back to Passbolt\n     *\n     * @param string $tokenType Authentication token type, which determines which endpoint to redirect users to\n     * @return string\n     */\n    public function getCallbackRedirectUrl(string $tokenType): string\n    {\n        if (!Validation::inList($tokenType, MfaDuoCallbackAuthenticationTokenService::$ALLOWED_TOKEN_TYPES)) {\n            $readableAllowedTokenTypes = implode(', ', MfaDuoCallbackAuthenticationTokenService::$ALLOWED_TOKEN_TYPES);\n            $msg = 'The authentication token type should be one of the following: ' . $readableAllowedTokenTypes . '.';\n            throw new InvalidArgumentException($msg);\n        }\n        $path = $tokenType === AuthenticationToken::TYPE_MFA_SETUP ? 'setup' : 'verify';\n        $url = '/mfa/' . $path . '/duo/callback';","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltCe/MultiFactorAuthentication/src/Service/Duo/MfaDuoGetSdkClientService.php#L35-L71","documentation":"MfaDuoGetSdkClientService::getOrFail instantiates the Duo Universal SDK Client with the org settings' client secret, client id, API hostname and callback redirect URL. If the Duo SDK throws DuoException during construction (invalid or missing credentials, malformed hostname), it is wrapped in this InternalErrorException. The organization's Duo configuration is therefore syntactically present but rejected by the SDK.","triggerScenarios":"Constructing the SDK client with DuoException raised — an empty or malformed client secret/client id, an api hostname that is not a valid *.duo.com host, or the DuoUniversal Client validating the integration and failing.","commonSituations":"Admin typo'd the Duo API hostname (e.g. missing '-s1' suffix or left the placeholder); client secret copied with trailing whitespace or newline; settings saved from an older format missing new keys; secret rotated in the Duo admin console but not updated in passbolt.","solutions":["Check getPrevious()/the DuoException message for which credential the SDK rejected","Re-enter the Duo Client ID, Client Secret, and API Hostname exactly as shown in the Duo Admin Panel application details","Trim whitespace/newlines from the copied secret and confirm the hostname has no https:// prefix or trailing slash","Run the SDK client construction with the same values in a small script/isolated test to reproduce outside the request cycle","Verify the Duo application is not disabled and its secret was not rotated recently"],"exampleFix":"// before (org settings)\n'apiHostname' => 'https://api-xxxxxxxx.duosecurity.com', // wrong: scheme included\n// after\n'apiHostname' => 'api-xxxxxxxx.duosecurity.com',\n'clientId' => 'DIXXXXXXXXXXXXXXXXXX',\n'clientSecret' => 'trimmed-secret-without-newlines',","handlingStrategy":"validation","validationCode":"$hostname = trim($settings->getDuoApiHostname());\nif ($hostname === '' || str_starts_with($hostname, 'http')) {\n    throw new \\InvalidArgumentException('Duo API hostname must be a bare host, no scheme.');\n}\nif (trim($settings->getDuoClientSecret()) === '' || trim($settings->getClientId()) === '') {\n    throw new \\InvalidArgumentException('Duo client id/secret are required.');\n}","typeGuard":null,"tryCatchPattern":"try {\n    $client = (new MfaDuoGetSdkClientService())->getOrFail($settings, $tokenType);\n} catch (\\Cake\\Http\\Exception\\InternalErrorException $e) {\n    $this->log('Duo settings rejected: ' . $e->getPrevious()?->getMessage());\n    // prompt admin to re-enter Duo org settings\n}","preventionTips":["Paste Duo credentials with a trim() and verify against the Duo Admin Panel values","Store the API hostname without scheme or trailing slash","Re-test the SDK client construction after rotating Duo secrets","Validate settings with the SDK in a pre-save check in the admin settings flow"],"tags":["configuration","duo","sdk-client","validation","credentials"],"backgroundTag":"invalid-config-value","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"}