passbolt/passbolt_api · error · BadRequestException
The SSO authentication token is invalid. User id mismatch.
Error message
The SSO authentication token is invalid. User id mismatch.
What it means
A BadRequestException from assert() when the token's user_id does not match the requesting user's id from the ExtendedUserAccessControl (or the token's user_id is not a valid UUID). This check guarantees a token can only be used by the user it was issued to.
Solutions
- Complete the SSO flow in the same browser session/user context that initiated it
- Regenerate the token for the currently authenticated user
- Verify the UAC is constructed with the correct user id (ExtendedUserAccessControl built from the right session/user entity)
- Log out other sessions and restart the SSO flow if the account was switched mid-flow
Example fix
// before $uac = new ExtendedUserAccessControl($wrongUserId, $userIp, $userAgent); $service->assertAndConsume($token, $uac, $settingsId); // user id mismatch // after $uac = new ExtendedUserAccessControl($token->user_id, $userIp, $userAgent); $service->assertAndConsume($token, $uac, $settingsId);
Defensive patterns
Strategy: validation
Validate before calling
if ($token->user_id !== $uac->getId()) {
throw new \Cake\Http\Exception\BadRequestException('Token belongs to another user');
} Try / catch
try {
$service->assertAndConsume($token, $uac, $settingsId);
} catch (\Cake\Http\Exception\BadRequestException $e) {
if (str_contains($e->getMessage(), 'User id mismatch')) {
// re-issue a token for the currently authenticated user
}
} Prevention
- Build ExtendedUserAccessControl from the authenticated session user, never raw input
- Re-authenticate the user if the session may have switched accounts
- Never share SSO verification links between users
When it happens
Trigger: assert()/assertAndConsume() with a $uac whose getId() differs from $token->user_id — e.g. a different logged-in session completes the SSO callback, or the token was issued for user A while the verify/recover request runs under user B.
Common situations: User switches accounts in another browser tab before completing SSO; session cookie belongs to a different user than the one who initiated SSO; sharing/replaying someone else's verification link; ID confusion between users table UUID and external IdP subject identifier.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Only administrators can create SSO settings.
- The SSO authentication token is invalid. Settings id is…
- The token is required in URL parameters.
- Access restricted to administrators.
- Access to this service requires an invitation. Please…
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/d5a33b897bab20f1.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltEe/Sso/src/Service/SsoAuthenticationTokens/SsoAuthenticationTokenGetService.php:184
* @throws \Cake\Http\Exception\BadRequestException if the SSO settings is not valid or not matching
* @return void
*/
public function assert(SsoAuthenticationToken $token, ExtendedUserAccessControl $uac, string $settingsId): void
{
$errorMsg = __('The SSO authentication token is invalid.') . ' ';
if ($token->isExpired()) {
throw new BadRequestException($errorMsg . __('The authentication token is expired.'));
}
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) {View on GitHub (pinned to 31c1bbc10f)