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
- Generate the nonce with the passbolt client SDK / server-issued state flow rather than crafting it manually
- Inspect the nonce sent in the request: ensure it is a non-empty string in the exact format SsoState::isValidState() expects
- Clear frontend cache/upgrade the SSO web plugin so it produces valid nonces
- 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
- Always obtain the nonce from the server's SSO state endpoint
- Never hand-craft or regenerate state values client-side
- Log nonce length/charset when debugging SSO failures
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
- Could not validate the SSO recover request.
- Invalid status.
- Service provider missing.
- Service provider not supported.
- Something went wrong when validating the single-sign on…
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)