passbolt/passbolt_api · error · FormValidationException
Unable to validate the Duo callback data.
Error message
Unable to validate the Duo callback data.
What it means
Thrown by DuoSetupCallbackGetController::getAndAssertMfaDuoCallbackData when MfaDuoCallbackForm::execute returns false, meaning the Duo callback payload failed form validation (required fields missing or invalid). A FormValidationException carrying the form errors is raised.
Solutions
- Restart the Duo MFA setup flow to get a fresh, complete callback redirect
- Inspect the exception's form errors to see which callback fields failed validation
- Ensure the Duo application's redirect/callback URL matches the passbolt route and no proxy strips query strings
- Do not call the callback endpoint directly; it must be reached via Duo's redirect
Defensive patterns
Strategy: try-catch
Validate before calling
const required = ['state', 'duo_code']; // per MfaDuoCallbackForm
const params = new URLSearchParams(window.location.search);
if (required.some((k) => !params.get(k))) {
// incomplete callback; restart the setup flow
} Type guard
function hasDuoCallbackParams(search) {
const p = new URLSearchParams(search);
return ['state'].every((k) => typeof p.get(k) === 'string' && p.get(k).length > 0);
} Try / catch
try {
await mfaService.completeDuoSetup(callbackParams);
} catch (e) {
if (e.formErrors) {
// show field errors and restart the Duo setup flow
}
} Prevention
- Always reach the callback via Duo's redirect, never by manual URL entry
- Verify proxies don't strip query strings from the callback route
- Restart the setup flow whenever the state nonce is missing or stale
When it happens
Trigger: Duo (or a hand-crafted request) hitting the Duo setup callback endpoint without the expected query parameters — missing code/state nonce, truncated redirect URL, or an attacker-probed request to the callback.
Common situations: Reverse proxy or bot filtering stripping callback query parameters; user bookmarking/pasting a partial callback URL; Duo state expired so the client receives a malformed retry; SSR tests hitting the endpoint directly without parameters.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Unable to validate the Duo callback data.
- Unable to authenticate to Duo.
- Unable to authenticate to Duo.
- A Duo state cookie is required.
- A Duo state cookie is required.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/cf4d66150317bbc6.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltCe/MultiFactorAuthentication/src/Controller/Duo/DuoSetupCallbackGetController.php:170
* @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
*/
private function consumeAndAssertCookieToken(): string
{
$cookieToken = (new MfaDuoStateCookieService())->readDuoStateCookieValue($this->getRequest());
if (is_null($cookieToken)) {
throw new BadRequestException(__('A Duo state cookie is required.'));View on GitHub (pinned to 31c1bbc10f)