{"record":{"id":"54752b0407b336cb","repo":"passbolt/passbolt_api","slug":"the-sso-state-is-invalid-user-ip-mismatch","errorCode":null,"errorMessage":"The SSO state is invalid. User IP mismatch.","messagePattern":"The SSO state is invalid\\. User IP mismatch\\.","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"plugins/PassboltEe/Sso/src/Service/SsoStates/SsoStatesAssertService.php","lineNumber":102,"sourceCode":"    private function assert(SsoState $ssoState, string $ssoSettingsId, ExtendedUserAccessControl $uac): void\n    {\n        $errorMsg = __('The SSO state is invalid.') . ' ';\n\n        if (!SsoState::isValidState($ssoState->state)) {\n            throw new BadRequestException(trim($errorMsg));\n        }\n\n        if ($ssoState->isExpired()) {\n            throw new BadRequestException($errorMsg . __('The SSO state is expired.'));\n        }\n\n        if ($ssoState->user_id !== $uac->getId() || !Validation::uuid($ssoState->user_id)) {\n            throw new BadRequestException($errorMsg . __('User id mismatch.'));\n        }\n\n        if (Configure::read('passbolt.security.userIp')) {\n            if ($ssoState->ip !== $uac->getUserIp()) {\n                throw new BadRequestException($errorMsg . __('User IP mismatch.'));\n            }\n        }\n\n        if (Configure::read('passbolt.security.userAgent')) {\n            if ($ssoState->user_agent !== $uac->getUserAgent()) {\n                throw new BadRequestException($errorMsg . __('User agent mismatch.'));\n            }\n        }\n\n        if ($ssoState->sso_settings_id !== $ssoSettingsId || !Validation::uuid($ssoState->sso_settings_id)) {\n            throw new BadRequestException($errorMsg . __('Settings mismatch.'));\n        }\n    }\n\n    /**\n     * Same assertions but without user ID.\n     *\n     * @param \\Passbolt\\Sso\\Model\\Entity\\SsoState $ssoState SSO state entity.","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltEe/Sso/src/Service/SsoStates/SsoStatesAssertService.php#L84-L120","documentation":"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.","triggerScenarios":"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.","commonSituations":"VPN connect/disconnect mid-login; load-balanced setups where client IP forwarding is misconfigured; users on mobile networks switching cells.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if ($stateRecord->ip !== $currentUserIp) { /* state will be rejected — restart login */ }","typeGuard":null,"tryCatchPattern":"try { $svc->assertAndConsume($state, $settingsId, $uac); } catch (BadRequestException $e) { if (str_contains($e->getMessage(), 'User IP mismatch')) { /* restart flow */ } throw $e; }","preventionTips":["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."],"tags":["php","sso","ip-mismatch","security"],"backgroundTag":"invalid-identifier","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}