passbolt/passbolt_api · warning · BadRequestException

Could not save the SSO state, invalid nonce.

Error message

Could not save the SSO state, invalid nonce.

What it means

SsoStatesSetService::create() rejects the SSO state nonce before persisting it. SsoState::isValidState() validates the nonce format; anything not matching is treated as a tampered or malformed OAuth2 state parameter and a BadRequestException is thrown.

Solutions

  1. Generate the nonce with the passbolt client SDK / server-issued state flow rather than crafting it manually
  2. Inspect the nonce sent in the request: ensure it is a non-empty string in the exact format SsoState::isValidState() expects
  3. Clear frontend cache/upgrade the SSO web plugin so it produces valid nonces
  4. Log the incoming nonce (length, charset) to identify truncation or encoding issues

Example fix

// before (client hand-rolled state)
const nonce = Math.random().toString();
// after (use server-generated nonce from GET /sso/states response)
const { nonce } = await passbolt.sso.getServerState();
Defensive patterns

Strategy: validation

Validate before calling

if (!is_string($nonce) || $nonce === '' || !preg_match('/^[A-Za-z0-9\-._~]{40,}$/', $nonce)) { throw new \InvalidArgumentException('nonce format invalid'); }

Type guard

function isValidNonce(mixed $nonce): bool { return is_string($nonce) && $nonce !== ''; }

Try / catch

try { $state = $service->create($uac, $nonce); } catch (BadRequestException $e) { return $this->respondError(400, $e->getMessage()); }

Prevention

When it happens

Trigger: Calling POST /sso/states (or create()) with a nonce that is absent, empty, not a string, or fails SsoState::isValidState() format checks (length/charset).

Common situations: Client generated its own state value instead of using the server-provided one; state truncated by URL/proxy; a bot or scanner hitting the endpoint with random parameters; stale frontend code after an SSO plugin upgrade changed the nonce format.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at plugins/PassboltEe/Sso/src/Service/SsoStates/SsoStatesSetService.php:51

     * @param string $state State to store
     * @param string $type Type of SSO state.
     * @param string $ssoSettingsId SSO settings ID.
     * @param \App\Utility\ExtendedUserAccessControl $uac UAC object.
     * @return \Passbolt\Sso\Model\Entity\SsoState
     * @throws \Cake\Http\Exception\InternalErrorException When unable to create the sso state.
     */
    public function create(
        string $nonce,
        string $state,
        string $type,
        string $ssoSettingsId,
        ExtendedUserAccessControl $uac
    ): SsoState {
        /** @var \Passbolt\Sso\Model\Table\SsoStatesTable $ssoStatesTable */
        $ssoStatesTable = $this->fetchTable('Passbolt/Sso.SsoStates');

        if (!SsoState::isValidState($nonce)) {
            throw new BadRequestException(__('Could not save the SSO state, invalid nonce.'));
        }

        try {
            $ssoState = $ssoStatesTable->newEntity(
                [
                    'nonce' => $nonce,
                    'state' => $state,
                    'type' => $type,
                    'sso_settings_id' => $ssoSettingsId,
                    'user_id' => $uac->getId() ?? null,
                    'ip' => $uac->getUserIp(),
                    'user_agent' => $uac->getUserAgent(),
                    'deleted' => DateTime::now()->modify('+' . SsoState::getExpiryDuration()),
                ],
                [
                    'accessibleFields' => [
                        'nonce' => true,
                        'state' => true,

View on GitHub (pinned to 31c1bbc10f)