passbolt/passbolt_api · error · BadRequestException

The SSO state is invalid.

Error message

The SSO state is invalid.

What it means

Thrown by SsoStatesAssertService::assert when the state value stored in the SSO state record does not pass SsoState::isValidState(). The state links the browser's SSO redirect to a pending authentication attempt; an unrecognized state value means the record is corrupt or not a genuine state entry.

Solutions

  1. Restart the SSO login flow from the beginning to generate a fresh state.
  2. Clear the corrupted SSO state record/cookie and retry.
  3. Ensure the Sso plugin version is consistent across all app servers (no mixed deployments).
Defensive patterns

Strategy: try-catch

Validate before calling

if (!SsoState::isValidState($stateRecord->state)) { /* regenerate state before callback */ }

Try / catch

try { $svc->assertAndConsume($state, $settingsId, $uac); } catch (BadRequestException $e) { // restart SSO flow with a fresh state
    return redirect('/sso/login'); }

Prevention

When it happens

Trigger: Completing the SSO return flow with a state cookie/record whose 'state' field was tampered with, manually edited, or written by an incompatible passbolt version; replaying a state record that was already mutated.

Common situations: Users manipulating cookies during debugging; leftover state rows from a version upgrade with a changed state format; hitting the SSO callback with a forged state parameter.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at plugins/PassboltEe/Sso/src/Service/SsoStates/SsoStatesAssertService.php:89

        $this->consume($ssoState);
    }

    /**
     * Makes assertions against the SSO state entity, current user, and settings ID.
     * This is used to ensure data integrity between request user/client, settings.
     *
     * @param \Passbolt\Sso\Model\Entity\SsoState $ssoState SSO state entity.
     * @param string $ssoSettingsId SSO Settings ID.
     * @param \App\Utility\ExtendedUserAccessControl $uac UAC object.
     * @return void
     */
    private function assert(SsoState $ssoState, string $ssoSettingsId, ExtendedUserAccessControl $uac): void
    {
        $errorMsg = __('The SSO state is invalid.') . ' ';

        if (!SsoState::isValidState($ssoState->state)) {
            throw new BadRequestException(trim($errorMsg));
        }

        if ($ssoState->isExpired()) {
            throw new BadRequestException($errorMsg . __('The SSO state is expired.'));
        }

        if ($ssoState->user_id !== $uac->getId() || !Validation::uuid($ssoState->user_id)) {
            throw new BadRequestException($errorMsg . __('User id mismatch.'));
        }

        if (Configure::read('passbolt.security.userIp')) {
            if ($ssoState->ip !== $uac->getUserIp()) {
                throw new BadRequestException($errorMsg . __('User IP mismatch.'));
            }
        }

        if (Configure::read('passbolt.security.userAgent')) {
            if ($ssoState->user_agent !== $uac->getUserAgent()) {

View on GitHub (pinned to 31c1bbc10f)