passbolt/passbolt_api · error · BadRequestException
Unable to authenticate to Duo.
Error message
Unable to authenticate to Duo. {error} What it means
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.
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
Defensive patterns
Strategy: try-catch
Validate before calling
// Before reaching the callback, confirm Duo config matches the Duo application
const params = new URLSearchParams(window.location.search);
if (!params.get('code') || !params.get('state')) {
// malformed callback: restart setup instead of forwarding
} Try / catch
try {
await mfaService.completeDuoSetup(callbackParams);
} catch (e) {
if (e instanceof BadRequestException && /Unable to authenticate to Duo/.test(e.message)) {
// inspect e.message detail; typically restart setup after fixing credentials
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Unable to authenticate to Duo.
- The duo authentication origin endpoint does not match the…
- The duo authentication subscriber does not match the…
- The token should reference an active Duo callback…
- Unable to validate the Duo callback data.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/e2631c5b9dcf40ad.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltCe/MultiFactorAuthentication/src/Controller/Duo/DuoSetupCallbackGetController.php:165
/**
* Get the Mfa Duo Callback data from the query and assert them.
*
* @throws \App\Error\Exception\FormValidationException If the data provided on the query does not validate
* @throws \Cake\Http\Exception\BadRequestException If Duo was not able to authenticate the user and provided error details
* @return \Passbolt\MultiFactorAuthentication\Model\Dto\MfaDuoCallbackDto
*/
private function getAndAssertMfaDuoCallbackData(): MfaDuoCallbackDto
{
$mfaDuoCallbackData = $this->getRequest()->getQueryParams();
$mfaDuoCallbackForm = new DuoCallbackForm();
$isValid = $mfaDuoCallbackForm->execute($mfaDuoCallbackData);
$mfaDuoCallbackDto = new MfaDuoCallbackDto($mfaDuoCallbackForm->getData());
if ($mfaDuoCallbackDto->hasError()) {
$msg = __('Unable to authenticate to Duo.');
$msg .= " {$mfaDuoCallbackDto->formatError()}";
throw new BadRequestException($msg);
}
if (!$isValid) {
$msg = __('Unable to validate the Duo callback data.');
throw new FormValidationException($msg, $mfaDuoCallbackForm);
}
return $mfaDuoCallbackDto;
}
/**
* Consume the duo state cookie containing the user authentication token id and assert the format this one.
*
* @return string The token id stored in the cookie
* @throws \Cake\Http\Exception\BadRequestException if the cookie is not defined
* @throws \Cake\Http\Exception\BadRequestException if the cookie value is not a string
* @throws \Cake\Http\Exception\BadRequestException if the cookie value is not a valid uuid
*/View on GitHub (pinned to 31c1bbc10f)