bitwarden/server · error · Exception

AcrMissingOrInvalid

Error message

AcrMissingOrInvalid

What it means

Thrown in AccountController.FindUserFromExternalProviderAsync (line 467) when SsoConfigurationData.ExpectedReturnAcrValue is configured (non-empty) but the ACR (Authentication Context Class Reference) claim returned by the IdP is missing or does not match the expected value.

Source

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

        var provider = result.Properties.Items["scheme"];
        //Todo: Validate provider is a valid GUID with TryParse instead. When this is invalid it throws an exception
        var orgId = new Guid(provider);
        var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(orgId);
        if (ssoConfig == null || !ssoConfig.Enabled)
        {
            throw new Exception(_i18nService.T("OrganizationOrSsoConfigNotFound"));
        }

        var ssoConfigData = ssoConfig.GetData();
        var externalUser = result.Principal;

        // Validate acr claim against expectation before going further
        if (!string.IsNullOrWhiteSpace(ssoConfigData.ExpectedReturnAcrValue))
        {
            var acrClaim = externalUser.FindFirst(JwtClaimTypes.AuthenticationContextClassReference);
            if (acrClaim?.Value != ssoConfigData.ExpectedReturnAcrValue)
            {
                throw new Exception(_i18nService.T("AcrMissingOrInvalid"));
            }
        }

        // Ensure the NameIdentifier used is not a transient name ID, if so, we need a different attribute
        //  for the user identifier.
        static bool nameIdIsNotTransient(Claim c) => c.Type == ClaimTypes.NameIdentifier
                                                     && (c.Properties == null
                                                         || !c.Properties.TryGetValue(SamlPropertyKeys.ClaimFormat,
                                                             out var claimFormat)
                                                         || claimFormat != SamlNameIdFormats.Transient);

        // Try to determine the unique id of the external user (issued by the provider)
        // the most common claim type for that are the sub claim and the NameIdentifier
        // depending on the external provider, some other claim type might be used
        var customUserIdClaimTypes = ssoConfigData.GetAdditionalUserIdClaimTypes();
        var userIdClaim = externalUser.FindFirst(c => customUserIdClaimTypes.Contains(c.Type)) ??
                          externalUser.FindFirst(JwtClaimTypes.Subject) ??
                          externalUser.FindFirst(nameIdIsNotTransient) ??

View on GitHub (pinned to e93b962371)

Solutions

  1. Verify the ExpectedReturnAcrValue in the org's SSO configuration matches what the IdP actually emits.
  2. Check the IdP's response (decode the id_token or inspect SAML response) for the actual ACR value.
  3. If the IdP does not support the configured ACR level, update or clear ExpectedReturnAcrValue in the SSO config.
  4. Ensure the IdP authentication policy enforces the required assurance level so the correct ACR is emitted.
Defensive patterns

Strategy: validation

Validate before calling

// Verify ACR expectation is met before proceeding
if (!string.IsNullOrWhiteSpace(ssoConfigData.ExpectedReturnAcrValue))
{
    var acr = externalUser.FindFirst(JwtClaimTypes.AuthenticationContextClassReference);
    if (acr?.Value != ssoConfigData.ExpectedReturnAcrValue)
        return BadRequest("ACR claim does not meet the required authentication level.");
}

Try / catch

try { await FindUserFromExternalProviderAsync(result); }
catch (Exception ex) when (ex.Message.Contains("AcrMissingOrInvalid"))
{ /* alert admin: IdP ACR config mismatch */ }

Prevention

When it happens

Trigger: The org's SSO config sets ExpectedReturnAcrValue (e.g., requiring a specific authentication assurance level), and the external user's claims either lack the JwtClaimTypes.AuthenticationContextClassReference claim or its value differs.

Common situations: IdP was reconfigured and no longer emits the expected ACR value; ExpectedReturnAcrValue was set incorrectly (typo, wrong value for the IdP); the IdP's authentication policy changed so a different ACR is returned; OIDC vs SAML ACR representation mismatch.

Related errors


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