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
- Verify the ExpectedReturnAcrValue in the org's SSO configuration matches what the IdP actually emits.
- Check the IdP's response (decode the id_token or inspect SAML response) for the actual ACR value.
- If the IdP does not support the configured ACR level, update or clear ExpectedReturnAcrValue in the SSO config.
- 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
- Verify ExpectedReturnAcrValue matches the IdP's actual ACR emission.
- Test the full SSO flow after changing IdP authentication policies.
- Document the IdP's ACR values and map them to the SSO config accurately.
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.