passbolt/passbolt_api · error · BadRequestException
The SSO state is invalid. User IP mismatch.
Error message
The SSO state is invalid. User IP mismatch.
What it means
Thrown by SsoStatesAssertService::assert when passbolt.security.userIp is enabled and the IP address stored in the SSO state differs from the current user's IP. The state is bound to the originating client IP to prevent the state being replayed from a different machine.
Solutions
- Retry the SSO login without changing network/VPN mid-flow.
- Configure trusted proxies so passbolt resolves the real client IP (security.trustedProxies / App.fullBaseUrl behind reverse proxy).
- If your deployment legitimately changes client IPs, verify whether disabling passbolt.security.userIp is acceptable security-wise.
Defensive patterns
Strategy: try-catch
Validate before calling
if ($stateRecord->ip !== $currentUserIp) { /* state will be rejected — restart login */ } Try / catch
try { $svc->assertAndConsume($state, $settingsId, $uac); } catch (BadRequestException $e) { if (str_contains($e->getMessage(), 'User IP mismatch')) { /* restart flow */ } throw $e; } Prevention
- Avoid switching networks/VPN mid-login.
- Configure trusted reverse proxies to forward X-Forwarded-For correctly.
- Pin the whole SSO flow to a stable network connection.
- Only relax passbolt.security.userIp after a security review.
When it happens
Trigger: User starts SSO login on one network (e.g. Wi-Fi) and completes the IdP callback on another (e.g. mobile data / VPN toggle); reverse proxy not passing X-Forwarded-For so passbolt sees the proxy IP instead of the client IP.
Common situations: VPN connect/disconnect mid-login; load-balanced setups where client IP forwarding is misconfigured; users on mobile networks switching cells.
Related errors
- The SSO authentication token is invalid. User IP mismatch.
- The SSO state is invalid. User id mismatch.
- Service provider invalid.
- Service provider missing.
- Service provider not supported.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/54752b0407b336cb.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltEe/Sso/src/Service/SsoStates/SsoStatesAssertService.php:102
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()) {
throw new BadRequestException($errorMsg . __('User agent mismatch.'));
}
}
if ($ssoState->sso_settings_id !== $ssoSettingsId || !Validation::uuid($ssoState->sso_settings_id)) {
throw new BadRequestException($errorMsg . __('Settings mismatch.'));
}
}
/**
* Same assertions but without user ID.
*
* @param \Passbolt\Sso\Model\Entity\SsoState $ssoState SSO state entity.View on GitHub (pinned to 31c1bbc10f)