passbolt/passbolt_api · warning · BadRequestException
The SSO authentication token is invalid. User IP mismatch.
Error message
The SSO authentication token is invalid. User IP mismatch.
What it means
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.
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
Example fix
// before
// passbolt.php missing proxy trusted proxies config -> wrong detected IP
// after
'App' => [
'fullBaseUrl' => 'https://passbolt.example.com',
],
// ensure trusted proxies are configured so getUserIp() matches the IP stored at token creation Defensive patterns
Strategy: try-catch
Validate before calling
$ipMatches = !\Cake\Core\Configure::read('passbolt.security.userIp')
|| $token->getDataProperty(\Passbolt\Sso\Model\Entity\SsoAuthenticationToken::DATA_IP) === $uac->getUserIp(); Try / catch
try {
$service->assertAndConsume($token, $uac, $settingsId);
} catch (\Cake\Http\Exception\BadRequestException $e) {
if (str_contains($e->getMessage(), 'User IP mismatch')) {
// ask the user to restart SSO from a stable network
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- The SSO state is invalid. User IP mismatch.
- Cannot parse JWKS endpoint response.
- Cannot parse JWKS endpoint response.
- Single sign-on failed. The provider address is not allowed.
- The SSO state is invalid. User agent mismatch.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/69c336764dcf04e2.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltEe/Sso/src/Service/SsoAuthenticationTokens/SsoAuthenticationTokenGetService.php:195
try {
$sid = $token->getDataProperty(SsoAuthenticationToken::DATA_SSO_SETTING_ID);
} catch (AuthenticationTokenDataPropertyException $exception) {
throw new BadRequestException($errorMsg . __('Settings id is missing.'), 400, $exception);
}
if ($token->user_id !== $uac->getId() || !Validation::uuid($token->user_id)) {
throw new BadRequestException($errorMsg . __('User id mismatch.'));
}
if (Configure::read('passbolt.security.userIp')) {
try {
$ip = $token->getDataProperty(SsoAuthenticationToken::DATA_IP);
} catch (AuthenticationTokenDataPropertyException $exception) {
throw new BadRequestException($errorMsg . __('Token IP is missing.'), 400, $exception);
}
if ($ip !== $uac->getUserIp()) {
throw new BadRequestException($errorMsg . __('User IP mismatch.'));
}
}
if (Configure::read('passbolt.security.userAgent')) {
try {
$ua = $token->getDataProperty(SsoAuthenticationToken::DATA_USER_AGENT);
} catch (AuthenticationTokenDataPropertyException $exception) {
throw new BadRequestException($errorMsg . __('User agent is missing.'), 400, $exception);
}
if ($ua !== $uac->getUserAgent()) {
throw new BadRequestException($errorMsg . __('User agent mismatch.'));
}
}
if ($sid !== $settingsId || !Validation::uuid($sid)) {
throw new BadRequestException($errorMsg . __('Settings mismatch.'));
}
}View on GitHub (pinned to 31c1bbc10f)