passbolt/passbolt_api · error · BadRequestException
The SSO authentication token is invalid. User agent…
Error message
The SSO authentication token is invalid. User agent mismatch.
What it means
Passbolt's SSO authentication token service validates that the browser user agent stored in the SSO authentication token data matches the user agent of the current request before consuming the token. When they differ, it concludes the token is being replayed from a different client and throws BadRequestException with 'User agent mismatch.' This protects the draft/SSO login flow against token theft across devices.
Solutions
- Redeem the SSO authentication token with the exact same User-Agent header that started the SSO flow
- If done programmatically (curl/SDK), copy the browser's User-Agent header into the token retrieval request
- Restart the SSO login flow from the same client so a fresh token is issued with the current user agent
- Check reverse-proxy/CDN config for User-Agent rewriting between requests
- In tests, set the request user agent explicitly to match the one used at token creation
Example fix
// before (curl, different UA) curl -H 'Authorization: <token>' https://passbolt/sso/keys // after curl -H 'Authorization: <token>' -H 'User-Agent: Mozilla/5.0 (same UA that started the SSO flow)' https://passbolt/sso/keys
Defensive patterns
Strategy: try-catch
Validate before calling
// client: ensure UA consistency before redeeming const ua = navigator.userAgent; if (storedUa !== ua) restartSsoFlow();
Try / catch
try {
$service->assertAndConsume($token, $uac, $settingsId);
} catch (BadRequestException $e) {
if (str_contains($e->getMessage(), 'User agent mismatch')) {
return $this->restartSsoFlow(); // new token with current UA
}
throw $e;
} Prevention
- Always redeem SSO tokens from the same browser/client that started the flow
- Copy the originating User-Agent header into programmatic requests
- Disable proxies/middleware that rewrite User-Agent between SSO steps
When it happens
Trigger: Calling SsoAuthenticationTokenGetService::assertAndConsume() (via the SSO draft/key retrieval endpoints) with a token whose DATA_USER_AGENT data property differs from the current request's User-Agent header, e.g. the SSO flow was started in one browser and the token is redeemed from another client or an HTTP client/curl call.
Common situations: A backend integration or test harness (curl, Postman, CI script) redeems the SSO token without reproducing the browser's exact User-Agent string; a proxy or middleware rewrites the User-Agent between the two SSO steps; the user switches browsers mid-login; token generated server-side in tests with a different UA.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- The SSO authentication token is invalid. Settings mismatch.
- Ajax/Json request not supported.
- Ajax/Json request not supported.
- Ajax/Json request not supported.
- Ajax/Json request not supported.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/d6fe287770da6def.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltEe/Sso/src/Service/SsoAuthenticationTokens/SsoAuthenticationTokenGetService.php:206
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)