passbolt/passbolt_api · error · FormValidationException

Unable to validate the Duo callback data.

Error message

Unable to validate the Duo callback data.

What it means

FormValidationException thrown by DuoVerifyCallbackGetController::getAndAssertMfaDuoCallbackData when MfaDuoCallbackForm->execute() returns false — the callback data failed server-side form validation. Unlike error 295, the DTO reported no formatted error; the form's validation errors are attached to the exception.

Solutions

  1. Check the exception's form errors to see which callback fields failed validation.
  2. Verify the Duo application's redirect/callback URL matches the passbolt route exactly.
  3. Restart the Duo verification flow so Duo re-issues complete callback parameters.
  4. Ensure no proxy or middleware strips query string parameters from the callback request.
  5. Confirm MFA Duo settings (state request key) are consistent with the Duo application config.
Defensive patterns

Strategy: try-catch

Validate before calling

// client-side pre-check that expected callback query params exist
const required = ['state', 'code'];
const params = new URLSearchParams(callbackUrl.split('?')[1]);
const missing = required.filter(k => !params.get(k));
if (missing.length) { throw new Error('Duo callback missing params: ' + missing.join(',')); }

Try / catch

try {
  await duoVerifyCallback();
} catch (e) {
  if (e.name === 'FormValidationException' || /Unable to validate the Duo callback data/.test(e.message)) {
    logFormErrors(e.errors); restartDuoVerifyFlow();
  } else { throw e; }
}

Prevention

When it happens

Trigger: GET /mfa/duo/verify/callback with missing or malformed query parameters required by MfaDuoCallbackForm (e.g. missing state, code, or duo fields), so the form cannot validate.

Common situations: Duo redirect truncated or manually edited callback URL; proxy stripping query parameters; wrong callback URL configured in the Duo application; mismatched state key between passbolt and Duo.

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


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/69ac404971356755. Report an issue: GitHub.

Appendix: source

Thrown at plugins/PassboltCe/MultiFactorAuthentication/src/Controller/Duo/DuoVerifyCallbackGetController.php:162

     * @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)