From be0754f5684bfea464c5a110c675dfecd6189778 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Fri, 27 Sep 2024 16:21:06 -0400 Subject: [PATCH] simplify configuration of external login providers --- Oqtane.Client/Modules/Admin/Users/Index.razor | 251 ++++++++++-------- .../Resources/Modules/Admin/Users/Index.resx | 13 +- Oqtane.Shared/Models/ExternalLoginProvider.cs | 11 + .../Shared/ExternalLoginProviders.cs | 56 ++++ 4 files changed, 223 insertions(+), 108 deletions(-) create mode 100644 Oqtane.Shared/Models/ExternalLoginProvider.cs create mode 100644 Oqtane.Shared/Shared/ExternalLoginProviders.cs diff --git a/Oqtane.Client/Modules/Admin/Users/Index.razor b/Oqtane.Client/Modules/Admin/Users/Index.razor index fa493c36..8892c6b3 100644 --- a/Oqtane.Client/Modules/Admin/Users/Index.razor +++ b/Oqtane.Client/Modules/Admin/Users/Index.razor @@ -182,11 +182,29 @@ else
-
+
+ +
+
+ + @if (!string.IsNullOrEmpty(_providerurl)) + { + @Localizer["Info"] + } +
+ +
+
+
@@ -452,6 +470,8 @@ else private string _maximumfailures; private string _lockoutduration; + private string _provider; + private string _providerurl; private string _providertype; private string _providername; private string _authority; @@ -519,33 +539,7 @@ else _maximumfailures = SettingService.GetSetting(settings, "IdentityOptions:Lockout:MaxFailedAccessAttempts", "5"); _lockoutduration = TimeSpan.Parse(SettingService.GetSetting(settings, "IdentityOptions:Lockout:DefaultLockoutTimeSpan", "00:05:00")).TotalMinutes.ToString(); - _providertype = SettingService.GetSetting(settings, "ExternalLogin:ProviderType", ""); - _providername = SettingService.GetSetting(settings, "ExternalLogin:ProviderName", ""); - _authority = SettingService.GetSetting(settings, "ExternalLogin:Authority", ""); - _metadataurl = SettingService.GetSetting(settings, "ExternalLogin:MetadataUrl", ""); - _authorizationurl = SettingService.GetSetting(settings, "ExternalLogin:AuthorizationUrl", ""); - _tokenurl = SettingService.GetSetting(settings, "ExternalLogin:TokenUrl", ""); - _userinfourl = SettingService.GetSetting(settings, "ExternalLogin:UserInfoUrl", ""); - _clientid = SettingService.GetSetting(settings, "ExternalLogin:ClientId", ""); - _clientsecret = SettingService.GetSetting(settings, "ExternalLogin:ClientSecret", ""); - _toggleclientsecret = SharedLocalizer["ShowPassword"]; - _authresponsetype = SettingService.GetSetting(settings, "ExternalLogin:AuthResponseType", "code"); - _scopes = SettingService.GetSetting(settings, "ExternalLogin:Scopes", ""); - _parameters = SettingService.GetSetting(settings, "ExternalLogin:Parameters", ""); - _pkce = SettingService.GetSetting(settings, "ExternalLogin:PKCE", "false"); - _redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype; - _reviewclaims = SettingService.GetSetting(settings, "ExternalLogin:ReviewClaims", "false"); - _externalloginurl = Utilities.TenantUrl(PageState.Alias, "/pages/external"); - _identifierclaimtype = SettingService.GetSetting(settings, "ExternalLogin:IdentifierClaimType", "sub"); - _nameclaimtype = SettingService.GetSetting(settings, "ExternalLogin:NameClaimType", "name"); - _emailclaimtype = SettingService.GetSetting(settings, "ExternalLogin:EmailClaimType", "email"); - _roleclaimtype = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimType", ""); - _roleclaimmappings = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimMappings", ""); - _synchronizeroles = SettingService.GetSetting(settings, "ExternalLogin:SynchronizeRoles", "false"); - _profileclaimtypes = SettingService.GetSetting(settings, "ExternalLogin:ProfileClaimTypes", ""); - _domainfilter = SettingService.GetSetting(settings, "ExternalLogin:DomainFilter", ""); - _createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "true"); - _verifyusers = SettingService.GetSetting(settings, "ExternalLogin:VerifyUsers", "true"); + LoadExternalLoginSettings(settings); _secret = SettingService.GetSetting(settings, "JwtOptions:Secret", ""); _togglesecret = SharedLocalizer["ShowPassword"]; @@ -555,6 +549,39 @@ else } } + private void LoadExternalLoginSettings(Dictionary settings) + { + _provider = SettingService.GetSetting(settings, "ExternalLogin:Provider", "Custom"); + _providerurl = SettingService.GetSetting(settings, "ExternalLogin:ProviderUrl", ""); + _providertype = SettingService.GetSetting(settings, "ExternalLogin:ProviderType", ""); + _providername = SettingService.GetSetting(settings, "ExternalLogin:ProviderName", ""); + _authority = SettingService.GetSetting(settings, "ExternalLogin:Authority", ""); + _metadataurl = SettingService.GetSetting(settings, "ExternalLogin:MetadataUrl", ""); + _authorizationurl = SettingService.GetSetting(settings, "ExternalLogin:AuthorizationUrl", ""); + _tokenurl = SettingService.GetSetting(settings, "ExternalLogin:TokenUrl", ""); + _userinfourl = SettingService.GetSetting(settings, "ExternalLogin:UserInfoUrl", ""); + _clientid = SettingService.GetSetting(settings, "ExternalLogin:ClientId", ""); + _clientsecret = SettingService.GetSetting(settings, "ExternalLogin:ClientSecret", ""); + _toggleclientsecret = SharedLocalizer["ShowPassword"]; + _authresponsetype = SettingService.GetSetting(settings, "ExternalLogin:AuthResponseType", "code"); + _scopes = SettingService.GetSetting(settings, "ExternalLogin:Scopes", ""); + _parameters = SettingService.GetSetting(settings, "ExternalLogin:Parameters", ""); + _pkce = SettingService.GetSetting(settings, "ExternalLogin:PKCE", "false"); + _redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype; + _reviewclaims = SettingService.GetSetting(settings, "ExternalLogin:ReviewClaims", "false"); + _externalloginurl = Utilities.TenantUrl(PageState.Alias, "/pages/external"); + _identifierclaimtype = SettingService.GetSetting(settings, "ExternalLogin:IdentifierClaimType", "sub"); + _nameclaimtype = SettingService.GetSetting(settings, "ExternalLogin:NameClaimType", "name"); + _emailclaimtype = SettingService.GetSetting(settings, "ExternalLogin:EmailClaimType", "email"); + _roleclaimtype = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimType", ""); + _roleclaimmappings = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimMappings", ""); + _synchronizeroles = SettingService.GetSetting(settings, "ExternalLogin:SynchronizeRoles", "false"); + _profileclaimtypes = SettingService.GetSetting(settings, "ExternalLogin:ProfileClaimTypes", ""); + _domainfilter = SettingService.GetSetting(settings, "ExternalLogin:DomainFilter", ""); + _createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "true"); + _verifyusers = SettingService.GetSetting(settings, "ExternalLogin:VerifyUsers", "true"); + } + private async Task LoadUsersAsync(bool load) { if (load) @@ -567,105 +594,117 @@ else users = users.OrderBy(u => u.User.DisplayName).ToList(); } } - } + } - private async Task DeleteUser(UserRole UserRole) - { - try - { - var user = await UserService.GetUserAsync(UserRole.UserId, PageState.Site.SiteId); - if (user != null) - { - await UserService.DeleteUserAsync(user.UserId, PageState.Site.SiteId); - await logger.LogInformation("User Deleted {User}", UserRole.User); - await LoadUsersAsync(true); - StateHasChanged(); - } - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Deleting User {User} {Error}", UserRole.User, ex.Message); - AddModuleMessage(ex.Message, MessageType.Error); - } - } + private async Task DeleteUser(UserRole UserRole) + { + try + { + var user = await UserService.GetUserAsync(UserRole.UserId, PageState.Site.SiteId); + if (user != null) + { + await UserService.DeleteUserAsync(user.UserId, PageState.Site.SiteId); + await logger.LogInformation("User Deleted {User}", UserRole.User); + await LoadUsersAsync(true); + StateHasChanged(); + } + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Deleting User {User} {Error}", UserRole.User, ex.Message); + AddModuleMessage(ex.Message, MessageType.Error); + } + } - private async Task SaveSiteSettings() - { - try - { - var site = PageState.Site; - site.AllowRegistration = bool.Parse(_allowregistration); - await SiteService.UpdateSiteAsync(site); + private async Task SaveSiteSettings() + { + try + { + var site = PageState.Site; + site.AllowRegistration = bool.Parse(_allowregistration); + await SiteService.UpdateSiteAsync(site); - var settings = await SettingService.GetSiteSettingsAsync(site.SiteId); - settings = SettingService.SetSetting(settings, "LoginOptions:AllowSiteLogin", _allowsitelogin, false); + var settings = await SettingService.GetSiteSettingsAsync(site.SiteId); + settings = SettingService.SetSetting(settings, "LoginOptions:AllowSiteLogin", _allowsitelogin, false); - if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) - { - settings = SettingService.SetSetting(settings, "LoginOptions:TwoFactor", _twofactor, false); - settings = SettingService.SetSetting(settings, "LoginOptions:CookieName", _cookiename, true); - settings = SettingService.SetSetting(settings, "LoginOptions:CookieExpiration", _cookieexpiration, true); - settings = SettingService.SetSetting(settings, "LoginOptions:AlwaysRemember", _alwaysremember, false); + if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) + { + settings = SettingService.SetSetting(settings, "LoginOptions:TwoFactor", _twofactor, false); + settings = SettingService.SetSetting(settings, "LoginOptions:CookieName", _cookiename, true); + settings = SettingService.SetSetting(settings, "LoginOptions:CookieExpiration", _cookieexpiration, true); + settings = SettingService.SetSetting(settings, "LoginOptions:AlwaysRemember", _alwaysremember, false); - settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequiredLength", _minimumlength, true); - settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequiredUniqueChars", _uniquecharacters, true); - settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireDigit", _requiredigit, true); - settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireUppercase", _requireupper, true); - settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireLowercase", _requirelower, true); - settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireNonAlphanumeric", _requirepunctuation, true); + settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequiredLength", _minimumlength, true); + settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequiredUniqueChars", _uniquecharacters, true); + settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireDigit", _requiredigit, true); + settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireUppercase", _requireupper, true); + settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireLowercase", _requirelower, true); + settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireNonAlphanumeric", _requirepunctuation, true); - settings = SettingService.SetSetting(settings, "IdentityOptions:Lockout:MaxFailedAccessAttempts", _maximumfailures, true); - settings = SettingService.SetSetting(settings, "IdentityOptions:Lockout:DefaultLockoutTimeSpan", TimeSpan.FromMinutes(Convert.ToInt64(_lockoutduration)).ToString(), true); + settings = SettingService.SetSetting(settings, "IdentityOptions:Lockout:MaxFailedAccessAttempts", _maximumfailures, true); + settings = SettingService.SetSetting(settings, "IdentityOptions:Lockout:DefaultLockoutTimeSpan", TimeSpan.FromMinutes(Convert.ToInt64(_lockoutduration)).ToString(), true); - settings = SettingService.SetSetting(settings, "ExternalLogin:ProviderType", _providertype, false); - settings = SettingService.SetSetting(settings, "ExternalLogin:ProviderName", _providername, false); - settings = SettingService.SetSetting(settings, "ExternalLogin:Authority", _authority, true); - settings = SettingService.SetSetting(settings, "ExternalLogin:MetadataUrl", _metadataurl, true); - settings = SettingService.SetSetting(settings, "ExternalLogin:AuthorizationUrl", _authorizationurl, true); - settings = SettingService.SetSetting(settings, "ExternalLogin:TokenUrl", _tokenurl, true); - settings = SettingService.SetSetting(settings, "ExternalLogin:UserInfoUrl", _userinfourl, true); - settings = SettingService.SetSetting(settings, "ExternalLogin:ClientId", _clientid, true); - settings = SettingService.SetSetting(settings, "ExternalLogin:ClientSecret", _clientsecret, true); + settings = SettingService.SetSetting(settings, "ExternalLogin:Provider", _provider, false); + settings = SettingService.SetSetting(settings, "ExternalLogin:ProviderType", _providertype, false); + settings = SettingService.SetSetting(settings, "ExternalLogin:ProviderName", _providername, false); + settings = SettingService.SetSetting(settings, "ExternalLogin:Authority", _authority, true); + settings = SettingService.SetSetting(settings, "ExternalLogin:MetadataUrl", _metadataurl, true); + settings = SettingService.SetSetting(settings, "ExternalLogin:AuthorizationUrl", _authorizationurl, true); + settings = SettingService.SetSetting(settings, "ExternalLogin:TokenUrl", _tokenurl, true); + settings = SettingService.SetSetting(settings, "ExternalLogin:UserInfoUrl", _userinfourl, true); + settings = SettingService.SetSetting(settings, "ExternalLogin:ClientId", _clientid, true); + settings = SettingService.SetSetting(settings, "ExternalLogin:ClientSecret", _clientsecret, true); settings = SettingService.SetSetting(settings, "ExternalLogin:AuthResponseType", _authresponsetype, true); settings = SettingService.SetSetting(settings, "ExternalLogin:Scopes", _scopes, true); - settings = SettingService.SetSetting(settings, "ExternalLogin:Parameters", _parameters, true); - settings = SettingService.SetSetting(settings, "ExternalLogin:PKCE", _pkce, true); + settings = SettingService.SetSetting(settings, "ExternalLogin:Parameters", _parameters, true); + settings = SettingService.SetSetting(settings, "ExternalLogin:PKCE", _pkce, true); settings = SettingService.SetSetting(settings, "ExternalLogin:ReviewClaims", _reviewclaims, true); settings = SettingService.SetSetting(settings, "ExternalLogin:IdentifierClaimType", _identifierclaimtype, true); settings = SettingService.SetSetting(settings, "ExternalLogin:NameClaimType", _nameclaimtype, true); settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true); - settings = SettingService.SetSetting(settings, "ExternalLogin:RoleClaimType", _roleclaimtype, true); + settings = SettingService.SetSetting(settings, "ExternalLogin:RoleClaimType", _roleclaimtype, true); settings = SettingService.SetSetting(settings, "ExternalLogin:RoleClaimMappings", _roleclaimmappings, true); settings = SettingService.SetSetting(settings, "ExternalLogin:SynchronizeRoles", _synchronizeroles, true); settings = SettingService.SetSetting(settings, "ExternalLogin:ProfileClaimTypes", _profileclaimtypes, true); - settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true); - settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true); + settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true); + settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true); settings = SettingService.SetSetting(settings, "ExternalLogin:VerifyUsers", _verifyusers, true); - settings = SettingService.SetSetting(settings, "JwtOptions:Secret", _secret, true); - settings = SettingService.SetSetting(settings, "JwtOptions:Issuer", _issuer, true); - settings = SettingService.SetSetting(settings, "JwtOptions:Audience", _audience, true); - settings = SettingService.SetSetting(settings, "JwtOptions:Lifetime", _lifetime, true); - } + settings = SettingService.SetSetting(settings, "JwtOptions:Secret", _secret, true); + settings = SettingService.SetSetting(settings, "JwtOptions:Issuer", _issuer, true); + settings = SettingService.SetSetting(settings, "JwtOptions:Audience", _audience, true); + settings = SettingService.SetSetting(settings, "JwtOptions:Lifetime", _lifetime, true); + } - await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId); - await SettingService.ClearSiteSettingsCacheAsync(); + await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId); + await SettingService.ClearSiteSettingsCacheAsync(); - if (!string.IsNullOrEmpty(_secret)) - { - SiteState.AuthorizationToken = await UserService.GetTokenAsync(); - } + if (!string.IsNullOrEmpty(_secret)) + { + SiteState.AuthorizationToken = await UserService.GetTokenAsync(); + } - AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message); - AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error); - } + AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message); + AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error); + } + } + + private void ProviderChanged(ChangeEventArgs e) + { + _provider = (string)e.Value; + var provider = Shared.ExternalLoginProviders.Providers.FirstOrDefault(item => item.Name == _provider); + if (provider != null) + { + LoadExternalLoginSettings(provider.Settings); + } + StateHasChanged(); } - - private void ProviderTypeChanged(ChangeEventArgs e) + + private void ProviderTypeChanged(ChangeEventArgs e) { _providertype = (string)e.Value; if (string.IsNullOrEmpty(_providername)) diff --git a/Oqtane.Client/Resources/Modules/Admin/Users/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Users/Index.resx index 46c19999..d168b641 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Users/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Users/Index.resx @@ -471,13 +471,22 @@ Review Claims? - + This option will record the full list of Claims returned by the Provider in the Event Log. It should only be used for testing purposes. External Login will be restricted when this option is enabled. Optionally specify the type name of the user's name claim provided by the identity provider. The typical value is 'name'. - + Name Claim: + + Select the external login provider + + + Provider: + + + Info + \ No newline at end of file diff --git a/Oqtane.Shared/Models/ExternalLoginProvider.cs b/Oqtane.Shared/Models/ExternalLoginProvider.cs new file mode 100644 index 00000000..8cda01d4 --- /dev/null +++ b/Oqtane.Shared/Models/ExternalLoginProvider.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Oqtane.Models +{ + public class ExternalLoginProvider + { + public string Name { get; set; } + + public Dictionary Settings { get; set; } + } +} diff --git a/Oqtane.Shared/Shared/ExternalLoginProviders.cs b/Oqtane.Shared/Shared/ExternalLoginProviders.cs new file mode 100644 index 00000000..643c4ea4 --- /dev/null +++ b/Oqtane.Shared/Shared/ExternalLoginProviders.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using Oqtane.Models; + +namespace Oqtane.Shared +{ + public class ExternalLoginProviders + { + public static List Providers + { + get + { + var providers = new List + { + new ExternalLoginProvider + { + Name = "Custom", + Settings = new Dictionary() + }, + new ExternalLoginProvider + { + Name = "Microsoft Entra", + Settings = new Dictionary() + { + { "ExternalLogin:ProviderUrl", "https://entra.microsoft.com" }, + { "ExternalLogin:ProviderType", "oidc" }, + { "ExternalLogin:ProviderName", "Microsoft Entra" }, + { "ExternalLogin:Authority", "https://login.microsoftonline.com/YOUR_TENANT_ID/v2.0" }, + { "ExternalLogin:ClientId", "YOUR CLIENT ID" }, + { "ExternalLogin:ClientSecret", "YOUR CLIENT SECRET" } + } + }, + new ExternalLoginProvider + { + Name = "GitHub", + Settings = new Dictionary() + { + { "ExternalLogin:ProviderUrl", "https://github.com/settings/developers#oauth-apps" }, + { "ExternalLogin:ProviderType", "oauth2" }, + { "ExternalLogin:ProviderName", "GitHub" }, + { "ExternalLogin:AuthorizationUrl", "https://github.com/login/oauth/authorize" }, + { "ExternalLogin:TokenUrl", "https://github.com/login/oauth/access_token" }, + { "ExternalLogin:UserInfoUrl", "https://api.github.com/user/emails" }, + { "ExternalLogin:ClientId", "YOUR CLIENT ID" }, + { "ExternalLogin:ClientSecret", "YOUR CLIENT SECRET" }, + { "ExternalLogin:Scopes", "user:email" }, + { "ExternalLogin:IdentifierClaimType", "email" }, + { "ExternalLogin:DomainFilter", "!users.noreply.github.com" } + } + } + }; + + return providers; + } + } + } +}