bitwarden/server · error · Exception
ExternalAuthenticationError
Error message
ExternalAuthenticationError
What it means
Thrown in AccountController.ExternalCallback (line 273) when HttpContext.AuthenticateAsync against the BitwardenExternalCookieAuthenticationScheme does not return a successful result. The temporary external cookie is set during the Challenge redirect and must be present and valid when the IdP redirects back.
Source
Thrown at bitwarden_license/src/Sso/Controllers/AccountController.cs:273
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);
// We will look these up as required (lazy resolution) to avoid multiple DB hits.
Organization? organization = null;
OrganizationUser? orgUser = null;
// The user has not authenticated with this SSO provider before.
// They could have an existing Bitwarden account in the User table though.
if (possibleSsoLinkedUser == null)
{
// FIXME: Update this file to be null safe and then delete the line below
#nullable disable
// If we're manually linking to SSO, the user's external identifier will be passed as query string parameter.
var userIdentifier = result.Properties.Items.Keys.Contains("user_identifier")View on GitHub (pinned to e93b962371)
Solutions
- Ensure the user navigates through the full Challenge → IdP → Callback flow; do not bookmark or directly visit the ExternalCallback URL.
- Check browser cookie settings and SameSite configuration for the external auth cookie.
- Verify the IdP redirect URI matches the configured callback exactly.
- Inspect the IdP's response for errors (check IdP logs and browser network tab).
Defensive patterns
Strategy: try-catch
Try / catch
try { await ExternalCallback(); }
catch (Exception ex) when (ex.Message.Contains("ExternalAuthenticationError"))
{
_logger.LogError(ex, "External auth failed during SSO callback");
return Redirect($"/login?error=sso_external_error");
} Prevention
- Ensure the user reaches ExternalCallback only through the full Challenge → IdP → redirect flow.
- Configure SameSite and Secure attributes on the external auth cookie correctly.
- Set an appropriate timeout on the external cookie to cover IdP login latency.
- Monitor IdP health and surface IdP errors to users clearly.
When it happens
Trigger: The external authentication cookie is missing, expired, corrupted, or the IdP returned an error that prevented a valid cookie from being set.
Common situations: Cookie blocked by browser privacy settings; user took too long and the temp cookie expired; load balancer or proxy stripping cookies; IdP returned an error response instead of a valid auth code; the callback URL was accessed directly without going through the Challenge flow.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/4b510a8614d63bf9.
Report an issue: GitHub.