bitwarden/server · error · Exception

InvalidUserIdentifier

Error message

InvalidUserIdentifier

What it means

Thrown during SSO login when the manual user-linking identifier string does not contain at least two comma-separated segments. The identifier is expected in the format 'userId,token' and is split on ','; if fewer than two parts result, the format is considered invalid. This path is reached only when a userIdentifier value is present in the SSO callback (manual linking flow) at AccountController.cs:549.

Source

Thrown at bitwarden_license/src/Sso/Controllers/AccountController.cs:789

                    OrganizationUserStatusType.Invited,
                    OrganizationUserStatusType.Accepted,
                    OrganizationUserStatusType.Confirmed,
                ],
                organization.DisplayName());
        }
        else
        {
            throw new Exception(_i18nService.T("CouldNotFindOrganizationUser", user.Id, organization.Id));
        }
    }

    private async Task<User?> GetUserFromManualLinkingDataAsync(string userIdentifier)
    {
        User? user = null;
        var split = userIdentifier.Split(",");
        if (split.Length < 2)
        {
            throw new Exception(_i18nService.T("InvalidUserIdentifier"));
        }

        var userId = split[0];
        var token = split[1];

        var tokenOptions = new TokenOptions();

        var claimedUser = await _userService.GetUserByIdAsync(userId);
        if (claimedUser != null)
        {
            var tokenIsValid = await _userManager.VerifyUserTokenAsync(
                claimedUser, tokenOptions.PasswordResetTokenProvider, TokenPurposes.LinkSso, token);
            if (tokenIsValid)
            {
                user = claimedUser;
            }
            else
            {

View on GitHub (pinned to e93b962371)

Solutions

  1. Ensure the userIdentifier passed to the SSO flow is always formatted as 'userId,token' with both segments non-empty.
  2. Audit the client or middleware that builds the manual linking data to confirm it appends the LinkSso token after the comma.
  3. If the token is missing, regenerate the SSO linking invitation from the organization admin console so a fresh token is issued.

Example fix

// before
var userIdentifier = userId.ToString();
// after
var token = await userManager.GenerateUserTokenAsync(user, tokenOptions.PasswordResetTokenProvider, TokenPurposes.LinkSso);
var userIdentifier = $"{user.Id},{token}";
Defensive patterns

Strategy: validation

Validate before calling

// Validate userIdentifier format before calling the SSO linking API
if (string.IsNullOrWhiteSpace(userIdentifier) ||
    userIdentifier.Split(',').Length < 2 ||
    string.IsNullOrWhiteSpace(userIdentifier.Split(',')[0]) ||
    string.IsNullOrWhiteSpace(userIdentifier.Split(',')[1]))
{
    return BadRequest("userIdentifier must be in 'userId,token' format with both parts non-empty.");
}

Type guard

// C# has no runtime type guard for strings; use a validation method
static bool IsValidUserIdentifier(string identifier)
    => !string.IsNullOrWhiteSpace(identifier)
       && identifier.Split(',').Length >= 2
       && identifier.Split(',').All(p => !string.IsNullOrWhiteSpace(p));

Try / catch

try { var user = await GetUserFromManualLinkingDataAsync(userIdentifier); }
catch (Exception ex) when (ex.Message.Contains("InvalidUserIdentifier"))
{ /* Show user-friendly 'linking data is malformed' message, prompt re-invitation */ }

Prevention

When it happens

Trigger: An SSO authentication callback carries a userIdentifier that is empty, has no comma, or is a single token. This happens when the linking URL/state is truncated, manually edited, or generated by a client that omits the token portion.

Common situations: A custom or older client constructs the linking payload without the LinkSso token. The user copies/pastes a partial linking URL. A bug in the calling code serializes only the userId without the comma-delimited token.

Related errors


AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13). Data as JSON: /api/errors/9e825b61a4230533. Report an issue: GitHub.