passbolt/passbolt_api · error · BadRequestException

The state is required in URL parameters.

Error message

The state is required in URL parameters.

What it means

The SSO callback requires a 'state' query parameter that must pass SsoState::isValidState(). If it is absent, not a string, or structurally invalid, the controller cannot proceed with the CSRF check and throws this 400.

Solutions

  1. Compare the redirect URL the provider sent against the one passbolt generated in stage1 — the state must be echoed verbatim.
  2. Fix the provider app configuration so the full callback URL (including state) is preserved on redirect.
  3. Restart the SSO flow to get a fresh state; do not hand-craft the callback URL.
  4. Check for middleware/proxy rewriting or decoding the query string (e.g. double URL-decoding corrupts the state).
Defensive patterns

Strategy: validation

Validate before calling

const state = new URL(callbackUrl).searchParams.get('state');
if (typeof state !== 'string' || state.length === 0) { throw new Error('Provider did not echo back the state parameter'); }

Type guard

function hasStateParam(url) { const s = new URL(url).searchParams.get('state'); return typeof s === 'string' && s.length > 0; }

Try / catch

try { await ssoCallback(url); } catch (e) { if (e.status === 400 && /state is required in URL/.test(e.message)) { checkProviderStateEcho(); restartFlow(); } else { throw e; } }

Prevention

When it happens

Trigger: GET to an SSO callback endpoint without ?state=... or with a malformed state value, typically when the identity provider does not echo back the state parameter it was given.

Common situations: Misconfigured OAuth2 provider (state not passed through on redirect); URL-encoding stripping/corrupting the state; users editing or shortening the callback URL; provider appending parameters in a way that mangles the query string.

Related errors


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

Appendix: source

Thrown at plugins/PassboltEe/Sso/src/Controller/AbstractSsoController.php:136

    public function getTokenFromUrlQuery(): string
    {
        $token = $this->request->getQuery('token');
        if (!is_string($token) || !OAuthTokenValidation::token($token)) {
            throw new BadRequestException(__('The token is required in URL parameters.'));
        }

        return $token;
    }

    /**
     * @throws \Cake\Http\Exception\BadRequestException if the state is not provided in URL query
     * @return string state
     */
    public function getStateFromUrlQuery(): string
    {
        $state = $this->request->getQuery('state');
        if (!is_string($state) || !SsoState::isValidState($state)) {
            throw new BadRequestException(__('The state is required in URL parameters.'));
        }

        return $state;
    }

    /**
     * @throws \Cake\Http\Exception\BadRequestException if the code (access token) is not provided in URL query
     * @return string code
     */
    public function getCodeFromUrlQuery(): string
    {
        $code = $this->request->getQuery('code');
        if (!isset($code) || !is_string($code)) {
            throw new BadRequestException(__('The code is required in URL parameters.'));
        }

        return $code;
    }

View on GitHub (pinned to 31c1bbc10f)