{"record":{"id":"69c336764dcf04e2","repo":"passbolt/passbolt_api","slug":"the-sso-authentication-token-is-invalid-user-ip-mismatch","errorCode":null,"errorMessage":"The SSO authentication token is invalid. User IP mismatch.","messagePattern":"The SSO authentication token is invalid\\. User IP mismatch\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"plugins/PassboltEe/Sso/src/Service/SsoAuthenticationTokens/SsoAuthenticationTokenGetService.php","lineNumber":195,"sourceCode":"        try {\n            $sid = $token->getDataProperty(SsoAuthenticationToken::DATA_SSO_SETTING_ID);\n        } catch (AuthenticationTokenDataPropertyException $exception) {\n            throw new BadRequestException($errorMsg . __('Settings id is missing.'), 400, $exception);\n        }\n\n        if ($token->user_id !== $uac->getId() || !Validation::uuid($token->user_id)) {\n            throw new BadRequestException($errorMsg . __('User id mismatch.'));\n        }\n\n        if (Configure::read('passbolt.security.userIp')) {\n            try {\n                $ip = $token->getDataProperty(SsoAuthenticationToken::DATA_IP);\n            } catch (AuthenticationTokenDataPropertyException $exception) {\n                throw new BadRequestException($errorMsg . __('Token IP is missing.'), 400, $exception);\n            }\n\n            if ($ip !== $uac->getUserIp()) {\n                throw new BadRequestException($errorMsg . __('User IP mismatch.'));\n            }\n        }\n\n        if (Configure::read('passbolt.security.userAgent')) {\n            try {\n                $ua = $token->getDataProperty(SsoAuthenticationToken::DATA_USER_AGENT);\n            } catch (AuthenticationTokenDataPropertyException $exception) {\n                throw new BadRequestException($errorMsg . __('User agent is missing.'), 400, $exception);\n            }\n            if ($ua !== $uac->getUserAgent()) {\n                throw new BadRequestException($errorMsg . __('User agent mismatch.'));\n            }\n        }\n\n        if ($sid !== $settingsId || !Validation::uuid($sid)) {\n            throw new BadRequestException($errorMsg . __('Settings mismatch.'));\n        }\n    }","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltEe/Sso/src/Service/SsoAuthenticationTokens/SsoAuthenticationTokenGetService.php#L177-L213","documentation":"A BadRequestException from assert() raised when 'passbolt.security.userIp' is enabled and the IP stored in the token's data differs from the client IP in the ExtendedUserAccessControl. This IP pinning prevents a token from being replayed from a different network location.","triggerScenarios":"assert()/assertAndConsume() where $ip (token DATA_IP) !== $uac->getUserIp(). Happens when the user's IP changed between starting the SSO flow and completing it — VPN connect/disconnect, mobile network switch, load-balanced requests hitting different egress IPs, or proxy header misconfiguration.","commonSituations":"Corporate VPN or NAT pools rotating IPs; requests behind different reverse proxies where X-Forwarded-For isn't consistently forwarded; IPv4 vs IPv6 mismatch (client switches between them); disabling the check via passbolt.security.userIp=false for such environments.","solutions":["Complete the SSO flow from the same network/IP used to initiate it","If behind proxies/load balancers, ensure X-Forwarded-For / client IP detection is consistent (Configure proxy detector in App config)","Set passbolt.security.userIp = false in config/passbolt.php if IP pinning is unsuitable for your environment, then restart the flow for a new token","Generate a fresh token after the network change"],"exampleFix":"// before\n// passbolt.php missing proxy trusted proxies config -> wrong detected IP\n// after\n'App' => [\n    'fullBaseUrl' => 'https://passbolt.example.com',\n],\n// ensure trusted proxies are configured so getUserIp() matches the IP stored at token creation","handlingStrategy":"try-catch","validationCode":"$ipMatches = !\\Cake\\Core\\Configure::read('passbolt.security.userIp')\n    || $token->getDataProperty(\\Passbolt\\Sso\\Model\\Entity\\SsoAuthenticationToken::DATA_IP) === $uac->getUserIp();","typeGuard":null,"tryCatchPattern":"try {\n    $service->assertAndConsume($token, $uac, $settingsId);\n} catch (\\Cake\\Http\\Exception\\BadRequestException $e) {\n    if (str_contains($e->getMessage(), 'User IP mismatch')) {\n        // ask the user to restart SSO from a stable network\n    }\n}","preventionTips":["Configure trusted proxies so client IP detection is stable behind load balancers","Avoid VPN/network switches mid-flow","Disable passbolt.security.userIp if clients have rotating IPs, and document the tradeoff"],"tags":["sso","ip-mismatch","security","network"],"backgroundTag":"invalid-config-value","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"}