mirror of
https://github.com/bitwarden/server
synced 2025-12-11 05:43:35 +00:00
feat(sso): [auth/pm-17719] Make SSO identifier errors consistent (#6345)
* feat(sso-account-controller): Make SSO identifiers consistent - align all return messages from prevalidate. * feat(shared-resources): Make SSO identifiers consistent - remove unused string resources, add new consistent error message. * feat(sso-account-controller): Make SSO identifiers consistent - Add logging.
This commit is contained in:
@@ -108,36 +108,32 @@ public class AccountController : Controller
|
|||||||
// Validate domain_hint provided
|
// Validate domain_hint provided
|
||||||
if (string.IsNullOrWhiteSpace(domainHint))
|
if (string.IsNullOrWhiteSpace(domainHint))
|
||||||
{
|
{
|
||||||
return InvalidJson("NoOrganizationIdentifierProvidedError");
|
_logger.LogError(new ArgumentException("domainHint is required."), "domainHint not specified.");
|
||||||
|
return InvalidJson("SsoInvalidIdentifierError");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate organization exists from domain_hint
|
// Validate organization exists from domain_hint
|
||||||
var organization = await _organizationRepository.GetByIdentifierAsync(domainHint);
|
var organization = await _organizationRepository.GetByIdentifierAsync(domainHint);
|
||||||
if (organization == null)
|
if (organization is not { UseSso: true })
|
||||||
{
|
{
|
||||||
return InvalidJson("OrganizationNotFoundByIdentifierError");
|
_logger.LogError("Organization not configured to use SSO.");
|
||||||
}
|
return InvalidJson("SsoInvalidIdentifierError");
|
||||||
if (!organization.UseSso)
|
|
||||||
{
|
|
||||||
return InvalidJson("SsoNotAllowedForOrganizationError");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate SsoConfig exists and is Enabled
|
// Validate SsoConfig exists and is Enabled
|
||||||
var ssoConfig = await _ssoConfigRepository.GetByIdentifierAsync(domainHint);
|
var ssoConfig = await _ssoConfigRepository.GetByIdentifierAsync(domainHint);
|
||||||
if (ssoConfig == null)
|
if (ssoConfig is not { Enabled: true })
|
||||||
{
|
{
|
||||||
return InvalidJson("SsoConfigurationNotFoundForOrganizationError");
|
_logger.LogError("SsoConfig not enabled.");
|
||||||
}
|
return InvalidJson("SsoInvalidIdentifierError");
|
||||||
if (!ssoConfig.Enabled)
|
|
||||||
{
|
|
||||||
return InvalidJson("SsoNotEnabledForOrganizationError");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate Authentication Scheme exists and is loaded (cache)
|
// Validate Authentication Scheme exists and is loaded (cache)
|
||||||
var scheme = await _schemeProvider.GetSchemeAsync(organization.Id.ToString());
|
var scheme = await _schemeProvider.GetSchemeAsync(organization.Id.ToString());
|
||||||
if (scheme == null || !(scheme is IDynamicAuthenticationScheme dynamicScheme))
|
if (scheme is not IDynamicAuthenticationScheme dynamicScheme)
|
||||||
{
|
{
|
||||||
return InvalidJson("NoSchemeOrHandlerForSsoConfigurationFoundError");
|
_logger.LogError("Invalid authentication scheme for organization.");
|
||||||
|
return InvalidJson("SsoInvalidIdentifierError");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run scheme validation
|
// Run scheme validation
|
||||||
@@ -147,13 +143,8 @@ public class AccountController : Controller
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
var translatedException = _i18nService.GetLocalizedHtmlString(ex.Message);
|
_logger.LogError(ex, "An error occurred while validating SSO dynamic scheme.");
|
||||||
var errorKey = "InvalidSchemeConfigurationError";
|
return InvalidJson("SsoInvalidIdentifierError");
|
||||||
if (!translatedException.ResourceNotFound)
|
|
||||||
{
|
|
||||||
errorKey = ex.Message;
|
|
||||||
}
|
|
||||||
return InvalidJson(errorKey, translatedException.ResourceNotFound ? ex : null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var tokenable = new SsoTokenable(organization, _globalSettings.Sso.SsoTokenLifetimeInSeconds);
|
var tokenable = new SsoTokenable(organization, _globalSettings.Sso.SsoTokenLifetimeInSeconds);
|
||||||
@@ -163,7 +154,8 @@ public class AccountController : Controller
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return InvalidJson("PreValidationError", ex);
|
_logger.LogError(ex, "An error occurred during SSO prevalidation.");
|
||||||
|
return InvalidJson("SsoInvalidIdentifierError");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,7 +344,7 @@ public class AccountController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to map the external identity to a Bitwarden user, through the SsoUser table, which holds the `externalId`.
|
/// Attempts to map the external identity to a Bitwarden user, through the SsoUser table, which holds the `externalId`.
|
||||||
/// The claims on the external identity are used to determine an `externalId`, and that is used to find the appropriate `SsoUser` and `User` records.
|
/// The claims on the external identity are used to determine an `externalId`, and that is used to find the appropriate `SsoUser` and `User` records.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task<(User user, string provider, string providerUserId, IEnumerable<Claim> claims, SsoConfigurationData config)>
|
private async Task<(User user, string provider, string providerUserId, IEnumerable<Claim> claims, SsoConfigurationData config)>
|
||||||
@@ -485,7 +477,7 @@ public class AccountController : Controller
|
|||||||
allowedStatuses: [OrganizationUserStatusType.Accepted, OrganizationUserStatusType.Confirmed]);
|
allowedStatuses: [OrganizationUserStatusType.Accepted, OrganizationUserStatusType.Confirmed]);
|
||||||
|
|
||||||
|
|
||||||
// Since we're in the auto-provisioning logic, this means that the user exists, but they have not
|
// Since we're in the auto-provisioning logic, this means that the user exists, but they have not
|
||||||
// authenticated with the org's SSO provider before now (otherwise we wouldn't be auto-provisioning them).
|
// authenticated with the org's SSO provider before now (otherwise we wouldn't be auto-provisioning them).
|
||||||
// We've verified that the user is Accepted or Confnirmed, so we can create an SsoUser link and proceed
|
// We've verified that the user is Accepted or Confnirmed, so we can create an SsoUser link and proceed
|
||||||
// with authentication.
|
// with authentication.
|
||||||
|
|||||||
@@ -394,24 +394,9 @@
|
|||||||
<data name="InvalidSchemeConfigurationError" xml:space="preserve">
|
<data name="InvalidSchemeConfigurationError" xml:space="preserve">
|
||||||
<value>The configured authentication scheme is not valid: "{0}"</value>
|
<value>The configured authentication scheme is not valid: "{0}"</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="NoSchemeOrHandlerForSsoConfigurationFoundError" xml:space="preserve">
|
|
||||||
<value>No scheme or handler for this SSO configuration found.</value>
|
|
||||||
</data>
|
|
||||||
<data name="SsoNotEnabledForOrganizationError" xml:space="preserve">
|
|
||||||
<value>SSO is not yet enabled for this organization.</value>
|
|
||||||
</data>
|
|
||||||
<data name="SsoConfigurationNotFoundForOrganizationError" xml:space="preserve">
|
|
||||||
<value>No SSO configuration exists for this organization.</value>
|
|
||||||
</data>
|
|
||||||
<data name="SsoNotAllowedForOrganizationError" xml:space="preserve">
|
|
||||||
<value>SSO is not allowed for this organization.</value>
|
|
||||||
</data>
|
|
||||||
<data name="OrganizationNotFoundByIdentifierError" xml:space="preserve">
|
<data name="OrganizationNotFoundByIdentifierError" xml:space="preserve">
|
||||||
<value>Organization not found from identifier.</value>
|
<value>Organization not found from identifier.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="NoOrganizationIdentifierProvidedError" xml:space="preserve">
|
|
||||||
<value>No organization identifier provided.</value>
|
|
||||||
</data>
|
|
||||||
<data name="InvalidAuthenticationOptionsForSaml2SchemeError" xml:space="preserve">
|
<data name="InvalidAuthenticationOptionsForSaml2SchemeError" xml:space="preserve">
|
||||||
<value>Invalid authentication options provided to SAML2 scheme.</value>
|
<value>Invalid authentication options provided to SAML2 scheme.</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -691,4 +676,7 @@
|
|||||||
<data name="InvalidSsoRedirectToken" xml:space="preserve">
|
<data name="InvalidSsoRedirectToken" xml:space="preserve">
|
||||||
<value>Single sign on redirect token is invalid or expired.</value>
|
<value>Single sign on redirect token is invalid or expired.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="SsoInvalidIdentifierError" xml:space="preserve">
|
||||||
|
<value>Invalid SSO identifier</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
Reference in New Issue
Block a user