bitwarden/server · error · Exception
SsoOrganizationIdMismatch
Error message
SsoOrganizationIdMismatch
What it means
Thrown in AccountController.ValidateSchemeAgainstSsoToken (line 260) when the scheme parameter is not a valid GUID or when the GUID it parses to does not match the OrganizationId embedded in the unprotected SSO token. This prevents a user from authenticating against one org's IdP while using another org's token.
Source
Thrown at bitwarden_license/src/Sso/Controllers/AccountController.cs:260
/// <param name="scheme">The authentication scheme (organization ID) to validate.</param>
/// <param name="ssoToken">The SSO token to validate against.</param>
/// <exception cref="Exception">Thrown if the scheme (organization ID) does not match the organization ID found in the ssoToken.</exception>
private void ValidateSchemeAgainstSsoToken(string scheme, string ssoToken)
{
SsoTokenable tokenable;
try
{
tokenable = _dataProtector.Unprotect(ssoToken);
}
catch
{
throw new Exception(_i18nService.T("InvalidSsoToken"));
}
if (!Guid.TryParse(scheme, out var schemeOrgId) || tokenable.OrganizationId != schemeOrgId)
{
throw new Exception(_i18nService.T("SsoOrganizationIdMismatch"));
}
}
[HttpGet]
public async Task<IActionResult> ExternalCallback()
{
// Read external identity from the temporary cookie
var result = await HttpContext.AuthenticateAsync(
AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme);
if (!result.Succeeded)
{
throw new Exception(_i18nService.T("ExternalAuthenticationError"));
}
// See if the user has logged in with this SSO provider before and has already been provisioned.
// This is signified by the user existing in the User table and the SSOUser table for the SSO provider they're using.
var (possibleSsoLinkedUser, provider, providerUserId, claims, ssoConfigData) = await FindUserFromExternalProviderAsync(result);View on GitHub (pinned to e93b962371)
Solutions
- Ensure the scheme passed to ExternalChallenge is exactly the organization ID that was used when the SSO token was minted in PreValidate.
- Do not modify or reuse the scheme parameter between token creation and challenge.
- Validate client-side that scheme is a valid GUID before navigating to ExternalChallenge.
Example fix
// before — mismatched scheme and token origin
RedirectToAction("ExternalChallenge", new { scheme = wrongOrgId, ssoToken });
// after — scheme comes from the same org as the token
var org = await _organizationRepository.GetByIdentifierAsync(domainHint);
RedirectToAction("ExternalChallenge", new { scheme = org.Id.ToString(), ssoToken }); Defensive patterns
Strategy: validation
Validate before calling
// Ensure scheme matches the token's org before challenge
if (!Guid.TryParse(scheme, out var schemeOrgId))
return BadRequest("Invalid scheme format.");
var tokenable = _dataProtector.Unprotect(ssoToken);
if (tokenable.OrganizationId != schemeOrgId)
return BadRequest("Scheme does not match SSO token organization."); Try / catch
try { ValidateSchemeAgainstSsoToken(scheme, ssoToken); }
catch (Exception ex) when (ex.Message.Contains("SsoOrganizationIdMismatch"))
{ /* log security event; restart flow with correct scheme */ } Prevention
- Derive the scheme from the same organization used to mint the SSO token.
- Treat scheme/token mismatches as potential tampering — log and alert.
- Do not allow client-side override of the scheme parameter.
When it happens
Trigger: ExternalChallenge receives a scheme that is not a GUID-parseable string, or whose parsed GUID differs from tokenable.OrganizationId in the unprotected token.
Common situations: scheme query param was manually changed or spoofed; client passed the wrong organization ID; token was minted for org A but the request targets org B; URL parameters got reordered or corrupted.
Related errors
- UserIdAndTokenMismatch
- InvalidReturnUrl
- InvalidSsoToken
- OrganizationOrSsoConfigNotFound
- AcrMissingOrInvalid
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/548b587c1c7468a4.
Report an issue: GitHub.