user identity improvements
This commit is contained in:
parent
c8ac4ec1e8
commit
3c33614115
|
@ -1,4 +1,5 @@
|
|||
@namespace Oqtane.Modules.Admin.Register
|
||||
@using System.Net
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IUserService UserService
|
||||
|
@ -88,9 +89,9 @@ else
|
|||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
{
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
|
||||
private async Task Register()
|
||||
{
|
||||
|
@ -120,7 +121,14 @@ else
|
|||
if (user != null)
|
||||
{
|
||||
await logger.LogInformation("User Created {Username} {Email}", _username, _email);
|
||||
AddModuleMessage(Localizer["Info.User.AccountCreate"], MessageType.Info);
|
||||
if (PageState.QueryString.ContainsKey("returnurl"))
|
||||
{
|
||||
NavigationManager.NavigateTo(WebUtility.UrlDecode(PageState.QueryString["returnurl"]));
|
||||
}
|
||||
else // legacy behavior
|
||||
{
|
||||
AddModuleMessage(Localizer["Info.User.AccountCreate"], MessageType.Info);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@namespace Oqtane.Modules.Admin.UserProfile
|
||||
@using System.Net
|
||||
@using System.Text.RegularExpressions;
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
|
@ -337,6 +338,11 @@
|
|||
email = PageState.User.Email;
|
||||
displayname = PageState.User.DisplayName;
|
||||
|
||||
if (string.IsNullOrEmpty(email))
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.User.NoEmail"], MessageType.Warning);
|
||||
}
|
||||
|
||||
// get user folder
|
||||
var folder = await FolderService.GetFolderAsync(ModuleState.SiteId, PageState.User.FolderPath);
|
||||
if (folder != null)
|
||||
|
@ -427,8 +433,15 @@
|
|||
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
||||
await logger.LogInformation("User Profile Saved");
|
||||
|
||||
AddModuleMessage(Localizer["Success.Profile.Update"], MessageType.Success);
|
||||
StateHasChanged();
|
||||
if (PageState.QueryString.ContainsKey("returnurl"))
|
||||
{
|
||||
NavigationManager.NavigateTo(WebUtility.UrlDecode(PageState.QueryString["returnurl"]));
|
||||
}
|
||||
else // legacy behavior
|
||||
{
|
||||
AddModuleMessage(Localizer["Success.Profile.Update"], MessageType.Success);
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -195,7 +195,7 @@ else
|
|||
@if (_providertype != "")
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="providername" HelpText="The external login provider name which will be displayed on the login page" ResourceKey="ProviderName">Provider Name:</Label>
|
||||
<Label Class="col-sm-3" For="providername" HelpText="Specify a friendly name for the external login provider which will be displayed on the Login page" ResourceKey="ProviderName">Provider Name:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="providername" class="form-control" @bind="@_providername" />
|
||||
</div>
|
||||
|
@ -300,41 +300,50 @@ else
|
|||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="reviewclaims" HelpText="This option should only be used for testing. It allows the full list of Claims returned by the Provider to be recorded in the Event Log. Please note that external login is restricted when this option is enabled." ResourceKey="ReviewClaims">Review Claims?</Label>
|
||||
<Label Class="col-sm-3" For="reviewclaims" HelpText="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." ResourceKey="ReviewClaims">Review Claims?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="reviewclaims" class="form-select" @bind="@_reviewclaims" required>
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
<div class="input-group">
|
||||
<select id="reviewclaims" class="form-select" @bind="@_reviewclaims" required>
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
@if (_reviewclaims == "true")
|
||||
{
|
||||
<a href="@_externalloginurl" target="_blank" class="btn btn-secondary">@SharedLocalizer["Test"]</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="identifierclaimtype" HelpText="The name of the unique user identifier claim provided by the provider" ResourceKey="IdentifierClaimType">Identifier Claim:</Label>
|
||||
<Label Class="col-sm-3" For="identifierclaimtype" HelpText="Specify the type name of the unique user identifier claim provided by the provider. The default value is 'sub'." ResourceKey="IdentifierClaimType">Identifier Claim:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="identifierclaimtype" class="form-control" @bind="@_identifierclaimtype" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="emailclaimtype" HelpText="The name of the email address claim provided by the provider" ResourceKey="EmailClaimType">Email Claim:</Label>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="nameclaimtype" HelpText="Optionally specify the type name of the user's name claim provided by the provider. The typical value is 'name'." ResourceKey="NameClaimType">Name Claim:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="nameclaimtype" class="form-control" @bind="@_nameclaimtype" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="emailclaimtype" HelpText="Optionally specify the type name of the email address claim provided by the provider. The typical value is 'email'," ResourceKey="EmailClaimType">Email Claim:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="emailclaimtype" class="form-control" @bind="@_emailclaimtype" />
|
||||
</div>
|
||||
</div>
|
||||
@if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="roleclaimtype" HelpText="The name of the role claim provided by the provider" ResourceKey="RoleClaimType">Role Claim:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="roleclaimtype" class="form-control" @bind="@_roleclaimtype" />
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="roleclaimtype" HelpText="The name of the role claim provided by the provider" ResourceKey="RoleClaimType">Role Claim:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="roleclaimtype" class="form-control" @bind="@_roleclaimtype" />
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="profileclaimtypes" HelpText="A comma delimited list of user profile claims provided by the provider, as well as mappings to your user profile definition. For example if the provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'." ResourceKey="ProfileClaimTypes">User Profile Claims:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="profileclaimtypes" class="form-control" @bind="@_profileclaimtypes" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="profileclaimtypes" HelpText="A comma delimited list of user profile claims provided by the provider, as well as mappings to your user profile definition. For example if the provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'." ResourceKey="ProfileClaimTypes">User Profile Claims:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="profileclaimtypes" class="form-control" @bind="@_profileclaimtypes" />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="domainfilter" HelpText="Provide any email domain filter criteria (separated by commas). Domains to exclude should be prefixed with an exclamation point (!). For example 'microsoft.com,!hotmail.com' would include microsoft.com email addresses but not hotmail.com email addresses." ResourceKey="DomainFilter">Domain Filter:</Label>
|
||||
<div class="col-sm-9">
|
||||
|
@ -443,7 +452,9 @@ else
|
|||
private string _pkce;
|
||||
private string _redirecturl;
|
||||
private string _reviewclaims;
|
||||
private string _externalloginurl;
|
||||
private string _identifierclaimtype;
|
||||
private string _nameclaimtype;
|
||||
private string _emailclaimtype;
|
||||
private string _roleclaimtype;
|
||||
private string _profileclaimtypes;
|
||||
|
@ -505,7 +516,9 @@ else
|
|||
_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", "");
|
||||
_profileclaimtypes = SettingService.GetSetting(settings, "ExternalLogin:ProfileClaimTypes", "");
|
||||
|
@ -598,7 +611,8 @@ else
|
|||
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:EmailClaimType", _emailclaimtype, 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:ProfileClaimTypes", _profileclaimtypes, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true);
|
||||
|
|
|
@ -204,8 +204,8 @@
|
|||
<data name="ExternalLoginStatus.DuplicateEmail" xml:space="preserve">
|
||||
<value>Multiple User Accounts Already Exist With The Email Address Of Your External Login. Please Contact Your Administrator For Further Instructions.</value>
|
||||
</data>
|
||||
<data name="ExternalLoginStatus.InvalidEmail" xml:space="preserve">
|
||||
<value>The External Login Provider Did Not Provide A Valid Email Address For Your Account. Please Contact Your Administrator For Further Instructions.</value>
|
||||
<data name="ExternalLoginStatus.MissingClaims" xml:space="preserve">
|
||||
<value>The External Login Provider Did Not Provide All Of The Required Information. Please Contact Your Administrator For Further Instructions.</value>
|
||||
</data>
|
||||
<data name="ExternalLoginStatus.ProviderKeyMismatch" xml:space="preserve">
|
||||
<value>An Error Occurred Verifying Your External Login. Please Contact Your Administrator For Further Instructions.</value>
|
||||
|
|
|
@ -147,6 +147,9 @@
|
|||
<data name="Message.User.NoLogIn" xml:space="preserve">
|
||||
<value>Current User Is Not Logged In</value>
|
||||
</data>
|
||||
<data name="Message.User.NoEmail" xml:space="preserve">
|
||||
<value>You Must Provide An Email Address For Your User Account</value>
|
||||
</data>
|
||||
<data name="Error.Profile.Load" xml:space="preserve">
|
||||
<value>Error Loading User Profile</value>
|
||||
</data>
|
||||
|
|
|
@ -247,7 +247,7 @@
|
|||
<value>Domain Filter:</value>
|
||||
</data>
|
||||
<data name="EmailClaimType.HelpText" xml:space="preserve">
|
||||
<value>The name of the email address claim provided by the identity provider</value>
|
||||
<value>Optionally specify the type name of the email address claim provided by the identity provider. The typical value is 'email'.</value>
|
||||
</data>
|
||||
<data name="EmailClaimType.Text" xml:space="preserve">
|
||||
<value>Email Claim:</value>
|
||||
|
@ -274,7 +274,7 @@
|
|||
<value>Use PKCE?</value>
|
||||
</data>
|
||||
<data name="ProviderName.HelpText" xml:space="preserve">
|
||||
<value>The external login provider name which will be displayed on the login page</value>
|
||||
<value>Specify a friendly name for the external login provider which will be displayed on the Login page</value>
|
||||
</data>
|
||||
<data name="ProviderName.Text" xml:space="preserve">
|
||||
<value>Provider Name:</value>
|
||||
|
@ -373,7 +373,7 @@
|
|||
<value>Last Login</value>
|
||||
</data>
|
||||
<data name="IdentifierClaimType.HelpText" xml:space="preserve">
|
||||
<value>The name of the unique user identifier claim provided by the identity provider</value>
|
||||
<value>Specify the type name of the unique user identifier claim provided by the identity provider. The default value is 'sub'.</value>
|
||||
</data>
|
||||
<data name="IdentifierClaimType.Text" xml:space="preserve">
|
||||
<value>Identifier Claim:</value>
|
||||
|
@ -385,13 +385,13 @@
|
|||
<value>Parameters:</value>
|
||||
</data>
|
||||
<data name="RoleClaimType.HelpText" xml:space="preserve">
|
||||
<value>Optionally provide the name of the role claim provided by the identity provider. These roles will be used in addition to any internal user roles assigned within the site.</value>
|
||||
<value>Optionally provide the type name of the role claim provided by the identity provider. These roles will be used in addition to any internal user roles assigned within the site.</value>
|
||||
</data>
|
||||
<data name="RoleClaimType.Text" xml:space="preserve">
|
||||
<value>Role Claim:</value>
|
||||
</data>
|
||||
<data name="ProfileClaimTypes.HelpText" xml:space="preserve">
|
||||
<value>Optionally provide a comma delimited list of user profile claims provided by the identity provider, as well as mappings to your user profile definition. For example if the identity provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'.</value>
|
||||
<value>Optionally provide a comma delimited list of user profile claim type names provided by the identity provider, as well as mappings to your user profile definition. For example if the identity provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'.</value>
|
||||
</data>
|
||||
<data name="ProfileClaimTypes.Text" xml:space="preserve">
|
||||
<value>User Profile Claims:</value>
|
||||
|
@ -460,6 +460,12 @@
|
|||
<value>Review Claims?</value>
|
||||
</data>
|
||||
<data name="ReviewClaims.HelpText" xml:space="preserve">
|
||||
<value>This option should only be used for testing. It allows the full list of Claims returned by the Provider to be recorded in the Event Log. Please note that external login is restricted when this option is enabled.</value>
|
||||
<value>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.</value>
|
||||
</data>
|
||||
<data name="NameClaimType.HelpText" xml:space="preserve">
|
||||
<value>Optionally specify the type name of the user's name claim provided by the identity provider. The typical value is 'name'.</value>
|
||||
</data>
|
||||
<data name="NameClaimType.Text" xml:space="preserve">
|
||||
<value>Name Claim:</value>
|
||||
</data>
|
||||
</root>
|
|
@ -435,4 +435,7 @@
|
|||
<data name="Uninstall" xml:space="preserve">
|
||||
<value>Uninstall</value>
|
||||
</data>
|
||||
<data name="Test" xml:space="preserve">
|
||||
<value>Test</value>
|
||||
</data>
|
||||
</root>
|
|
@ -26,8 +26,7 @@ namespace Oqtane.Themes.Controls
|
|||
var allowexternallogin = (SettingService.GetSetting(PageState.Site.Settings, "ExternalLogin:ProviderType", "") != "") ? true : false;
|
||||
var allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true"));
|
||||
|
||||
Route route = new Route(PageState.Uri.AbsoluteUri, PageState.Alias.Path);
|
||||
var returnurl = WebUtility.UrlEncode(route.PathAndQuery);
|
||||
var returnurl = WebUtility.UrlEncode(PageState.Route.PathAndQuery);
|
||||
|
||||
if (allowexternallogin && !allowsitelogin)
|
||||
{
|
||||
|
@ -39,7 +38,6 @@ namespace Oqtane.Themes.Controls
|
|||
// local login
|
||||
NavigationManager.NavigateTo(NavigateUrl("login", "?returnurl=" + returnurl));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected async Task LogoutUser()
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@namespace Oqtane.Themes.Controls
|
||||
@using System.Net
|
||||
@inherits ThemeControlBase
|
||||
@inject IStringLocalizer<UserProfile> Localizer
|
||||
|
||||
|
@ -26,14 +27,21 @@
|
|||
[Parameter]
|
||||
public bool ShowRegister { get; set; }
|
||||
|
||||
private string _returnurl = "";
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
_returnurl = WebUtility.UrlEncode(PageState.Route.PathAndQuery);
|
||||
}
|
||||
|
||||
private void RegisterUser()
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl("register"));
|
||||
NavigationManager.NavigateTo(NavigateUrl("register", "returnurl=" + _returnurl));
|
||||
}
|
||||
|
||||
private void UpdateProfile()
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl("profile"));
|
||||
NavigationManager.NavigateTo(NavigateUrl("profile", "returnurl=" + _returnurl));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@namespace Oqtane.UI
|
||||
@using System.Net
|
||||
@inject IJSRuntime JSRuntime
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject SiteState SiteState
|
||||
|
@ -87,6 +88,13 @@
|
|||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
// force user to provide email address (email may be missing if using external login)
|
||||
if (PageState.User != null && string.IsNullOrEmpty(PageState.User.Email) && PageState.Route.PagePath != "profile")
|
||||
{
|
||||
NavigationManager.NavigateTo(Utilities.NavigateUrl(PageState.Alias.Path, "profile", "returnurl=" + WebUtility.UrlEncode(PageState.Route.PathAndQuery)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!firstRender)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(PageState.Page.HeadContent) && PageState.Page.HeadContent.Contains("<script"))
|
||||
|
|
|
@ -176,12 +176,13 @@ namespace Oqtane.Controllers
|
|||
if (ModelState.IsValid && user.SiteId == _tenantManager.GetAlias().SiteId && user.UserId == id && _users.GetUser(user.UserId, false) != null
|
||||
&& (_userPermissions.IsAuthorized(User, user.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin) || User.Identity.Name == user.Username))
|
||||
{
|
||||
user.EmailConfirmed = User.IsInRole(RoleNames.Admin);
|
||||
user = await _userManager.UpdateUser(user);
|
||||
}
|
||||
else
|
||||
{
|
||||
user.Password = ""; // remove sensitive information
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Post Attempt {User}", user);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Put Attempt {User}", user);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
user = null;
|
||||
}
|
||||
|
|
|
@ -179,7 +179,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
options.Lockout.AllowedForNewUsers = false;
|
||||
|
||||
// SignIn settings
|
||||
options.SignIn.RequireConfirmedEmail = true;
|
||||
options.SignIn.RequireConfirmedEmail = true;
|
||||
options.SignIn.RequireConfirmedPhoneNumber = false;
|
||||
|
||||
// User settings
|
||||
|
|
|
@ -150,15 +150,16 @@ namespace Oqtane.Extensions
|
|||
private static async Task OnCreatingTicket(OAuthCreatingTicketContext context)
|
||||
{
|
||||
// OAuth 2.0
|
||||
var email = "";
|
||||
var id = "";
|
||||
var claims = "";
|
||||
var id = "";
|
||||
var name = "";
|
||||
var email = "";
|
||||
|
||||
if (context.Options.UserInformationEndpoint != "")
|
||||
{
|
||||
try
|
||||
{
|
||||
// call user information endpoint
|
||||
// call user information endpoint using access token
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
|
||||
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
request.Headers.UserAgent.Add(new ProductInfoHeaderValue(Constants.PackageId, Constants.Version));
|
||||
|
@ -167,32 +168,57 @@ namespace Oqtane.Extensions
|
|||
response.EnsureSuccessStatusCode();
|
||||
claims = await response.Content.ReadAsStringAsync();
|
||||
|
||||
// parse json output
|
||||
// get claim types
|
||||
var idClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:IdentifierClaimType", "");
|
||||
var nameClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:NameClaimType", "");
|
||||
var emailClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:EmailClaimType", "");
|
||||
if (!claims.StartsWith("[") && !claims.EndsWith("]"))
|
||||
|
||||
// some user endpoints can return multiple objects (ie. GitHub) so convert single object to array (if necessary)
|
||||
var jsonclaims = claims;
|
||||
if (!jsonclaims.StartsWith("[") && !jsonclaims.EndsWith("]"))
|
||||
{
|
||||
claims = "[" + claims + "]"; // convert to json array
|
||||
jsonclaims = "[" + jsonclaims + "]";
|
||||
}
|
||||
JsonNode items = JsonNode.Parse(claims)!;
|
||||
|
||||
// parse claim values
|
||||
JsonNode items = JsonNode.Parse(jsonclaims)!;
|
||||
foreach (var item in items.AsArray())
|
||||
{
|
||||
if (item[emailClaimType] != null)
|
||||
// id claim is required
|
||||
if (!string.IsNullOrEmpty(idClaimType) && item[idClaimType] != null)
|
||||
{
|
||||
if (EmailValid(item[emailClaimType].ToString(), context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:DomainFilter", "")))
|
||||
id = item[idClaimType].ToString();
|
||||
|
||||
// name claim is optional
|
||||
if (!string.IsNullOrEmpty(nameClaimType))
|
||||
{
|
||||
email = item[emailClaimType].ToString().ToLower();
|
||||
if (item[idClaimType] != null)
|
||||
if (item[nameClaimType] != null)
|
||||
{
|
||||
id = item[idClaimType].ToString();
|
||||
name = item[nameClaimType].ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
id = ""; // name claim was specified but was not provided
|
||||
}
|
||||
}
|
||||
|
||||
// email claim is optional
|
||||
if (!string.IsNullOrEmpty(emailClaimType))
|
||||
{
|
||||
if (item[emailClaimType] != null && EmailValid(item[emailClaimType].ToString(), context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:DomainFilter", "")))
|
||||
{
|
||||
email = item[emailClaimType].ToString().ToLower();
|
||||
}
|
||||
else
|
||||
{
|
||||
id = ""; // email claim was specified but was not provided or is invalid
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
id = email;
|
||||
if (!string.IsNullOrEmpty(id))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -203,7 +229,7 @@ namespace Oqtane.Extensions
|
|||
}
|
||||
|
||||
// validate user
|
||||
var identity = await ValidateUser(email, id, claims, context.HttpContext, context.Principal);
|
||||
var identity = await ValidateUser(id, name, email, claims, context.HttpContext, context.Principal);
|
||||
if (identity.Label == ExternalLoginStatus.Success)
|
||||
{
|
||||
identity.AddClaim(new Claim("access_token", context.AccessToken));
|
||||
|
@ -231,28 +257,53 @@ namespace Oqtane.Extensions
|
|||
private static async Task OnTokenValidated(TokenValidatedContext context)
|
||||
{
|
||||
// OpenID Connect
|
||||
var claims = "";
|
||||
var id = "";
|
||||
var name = "";
|
||||
var email = "";
|
||||
|
||||
// serialize claims
|
||||
foreach (var claim in context.Principal.Claims)
|
||||
{
|
||||
claims += "\"" + claim.Type + "\":\"" + claim.Value + "\",";
|
||||
}
|
||||
claims = "{" + claims.Substring(0, claims.Length - 1) + "}";
|
||||
|
||||
// get claim types
|
||||
var idClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:IdentifierClaimType", "");
|
||||
var id = context.Principal.FindFirstValue(idClaimType);
|
||||
var nameClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:NameClaimType", "");
|
||||
var emailClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:EmailClaimType", "");
|
||||
var email = context.Principal.FindFirstValue(emailClaimType);
|
||||
var claims = string.Join(", ", context.Principal.Claims.Select(item => item.Type).ToArray());
|
||||
|
||||
// parse claim values
|
||||
id = context.Principal.FindFirstValue(idClaimType); // required
|
||||
if (!string.IsNullOrEmpty(nameClaimType))
|
||||
{
|
||||
if (context.Principal.FindFirstValue(nameClaimType) != null)
|
||||
{
|
||||
name = context.Principal.FindFirstValue(nameClaimType);
|
||||
}
|
||||
else
|
||||
{
|
||||
id = ""; // name claim was specified but was not provided
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(emailClaimType))
|
||||
{
|
||||
if (context.Principal.FindFirstValue(emailClaimType) != null && EmailValid(context.Principal.FindFirstValue(emailClaimType), context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:DomainFilter", "")))
|
||||
{
|
||||
email = context.Principal.FindFirstValue(emailClaimType);
|
||||
}
|
||||
else
|
||||
{
|
||||
id = ""; // email claim was specified but was not provided or is invalid
|
||||
}
|
||||
}
|
||||
|
||||
// validate user
|
||||
var identity = await ValidateUser(email, id, claims, context.HttpContext, context.Principal);
|
||||
var identity = await ValidateUser(id, name, email, claims, context.HttpContext, context.Principal);
|
||||
if (identity.Label == ExternalLoginStatus.Success)
|
||||
{
|
||||
// external roles
|
||||
if (!string.IsNullOrEmpty(context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimType", "")))
|
||||
{
|
||||
foreach (var claim in context.Principal.Claims.Where(item => item.Type == ClaimTypes.Role))
|
||||
{
|
||||
if (!identity.Claims.Any(item => item.Type == ClaimTypes.Role && item.Value == claim.Value))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.Role, claim.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// include access token
|
||||
identity.AddClaim(new Claim("access_token", context.SecurityToken.RawData));
|
||||
context.Principal = new ClaimsPrincipal(identity);
|
||||
}
|
||||
|
@ -284,13 +335,13 @@ namespace Oqtane.Extensions
|
|||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static async Task<ClaimsIdentity> ValidateUser(string email, string id, string claims, HttpContext httpContext, ClaimsPrincipal claimsPrincipal)
|
||||
private static async Task<ClaimsIdentity> ValidateUser(string id, string name, string email, string claims, HttpContext httpContext, ClaimsPrincipal claimsPrincipal)
|
||||
{
|
||||
var _logger = httpContext.RequestServices.GetRequiredService<ILogManager>();
|
||||
ClaimsIdentity identity = new ClaimsIdentity(Constants.AuthenticationScheme);
|
||||
// use identity.Label as a temporary location to store validation status information
|
||||
|
||||
// review claims option (for testing)
|
||||
// review claims feature (for testing - external login is disabled)
|
||||
if (bool.Parse(httpContext.GetSiteSettings().GetValue("ExternalLogin:ReviewClaims", "false")))
|
||||
{
|
||||
_logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "Provider Returned The Following Claims: {Claims}", claims);
|
||||
|
@ -316,136 +367,158 @@ namespace Oqtane.Extensions
|
|||
}
|
||||
else
|
||||
{
|
||||
if (EmailValid(email, httpContext.GetSiteSettings().GetValue("ExternalLogin:DomainFilter", "")))
|
||||
bool duplicates = false;
|
||||
if (!string.IsNullOrEmpty(email))
|
||||
{
|
||||
bool duplicates = false;
|
||||
try
|
||||
{
|
||||
identityuser = await _identityUserManager.FindByEmailAsync(email);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// FindByEmailAsync will throw an error if the email matches multiple user accounts
|
||||
catch // FindByEmailAsync will throw an error if the email matches multiple user accounts
|
||||
{
|
||||
duplicates = true;
|
||||
}
|
||||
if (identityuser == null)
|
||||
}
|
||||
if (identityuser == null)
|
||||
{
|
||||
if (duplicates)
|
||||
{
|
||||
if (duplicates)
|
||||
identity.Label = ExternalLoginStatus.DuplicateEmail;
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Multiple Users Exist With Email Address {Email}. Login Denied.", email);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bool.Parse(httpContext.GetSiteSettings().GetValue("ExternalLogin:CreateUsers", "true")))
|
||||
{
|
||||
identity.Label = ExternalLoginStatus.DuplicateEmail;
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Multiple Users Exist With Email Address {Email}. Login Denied.", email);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bool.Parse(httpContext.GetSiteSettings().GetValue("ExternalLogin:CreateUsers", "true")))
|
||||
{
|
||||
identityuser = new IdentityUser();
|
||||
identityuser.UserName = email;
|
||||
identityuser.Email = email;
|
||||
identityuser.EmailConfirmed = true;
|
||||
var result = await _identityUserManager.CreateAsync(identityuser, DateTime.UtcNow.ToString("yyyy-MMM-dd-HH-mm-ss", CultureInfo.InvariantCulture));
|
||||
if (result.Succeeded)
|
||||
{
|
||||
user = new User
|
||||
{
|
||||
SiteId = alias.SiteId,
|
||||
Username = email,
|
||||
DisplayName = email,
|
||||
Email = email,
|
||||
LastLoginOn = null,
|
||||
LastIPAddress = ""
|
||||
};
|
||||
user = _users.AddUser(user);
|
||||
// user identifiers
|
||||
var username = "";
|
||||
var emailaddress = "";
|
||||
var displayname = "";
|
||||
bool emailconfirmed = false;
|
||||
|
||||
if (user != null)
|
||||
if (!string.IsNullOrEmpty(email)) // email claim provided
|
||||
{
|
||||
username = email;
|
||||
emailaddress = email;
|
||||
displayname = (!string.IsNullOrEmpty(name)) ? name : email;
|
||||
emailconfirmed = true;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(name)) // name claim provided
|
||||
{
|
||||
username = name.ToLower().Replace(" ", "") + DateTime.UtcNow.ToString("mmss");
|
||||
emailaddress = ""; // unknown - will need to be requested from user later
|
||||
displayname = name;
|
||||
}
|
||||
else // neither email nor name provided
|
||||
{
|
||||
username = Guid.NewGuid().ToString("N");
|
||||
emailaddress = ""; // unknown - will need to be requested from user later
|
||||
displayname = username;
|
||||
}
|
||||
|
||||
identityuser = new IdentityUser();
|
||||
identityuser.UserName = username;
|
||||
identityuser.Email = emailaddress;
|
||||
identityuser.EmailConfirmed = emailconfirmed;
|
||||
|
||||
// generate password based on random date and punctuation ie. Jan-23-1981+14:43:12!
|
||||
Random rnd = new Random();
|
||||
var date = DateTime.UtcNow.AddDays(-rnd.Next(50 * 365)).AddHours(rnd.Next(0, 24)).AddMinutes(rnd.Next(0, 60)).AddSeconds(rnd.Next(0, 60));
|
||||
var password = date.ToString("MMM-dd-yyyy+HH:mm:ss", CultureInfo.InvariantCulture) + (char)rnd.Next(33, 47);
|
||||
|
||||
var result = await _identityUserManager.CreateAsync(identityuser, password);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
user = new User
|
||||
{
|
||||
SiteId = alias.SiteId,
|
||||
Username = username,
|
||||
DisplayName = displayname,
|
||||
Email = emailaddress,
|
||||
LastLoginOn = null,
|
||||
LastIPAddress = ""
|
||||
};
|
||||
user = _users.AddUser(user);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(email))
|
||||
{
|
||||
var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>();
|
||||
string url = httpContext.Request.Scheme + "://" + alias.Name;
|
||||
string body = "You Recently Used An External Account To Sign In To Our Site.\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, user, "User Account Notification", body);
|
||||
_notifications.AddNotification(notification);
|
||||
|
||||
// add user login
|
||||
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(providerType + ":" + user.SiteId.ToString(), id, providerName));
|
||||
|
||||
_logger.Log(user.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "User Added {User}", user);
|
||||
}
|
||||
else
|
||||
{
|
||||
identity.Label = ExternalLoginStatus.UserNotCreated;
|
||||
_logger.Log(alias.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add User {Email}", email);
|
||||
}
|
||||
|
||||
// add user login
|
||||
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(providerType + ":" + user.SiteId.ToString(), id, providerName));
|
||||
|
||||
_logger.Log(user.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "User Added {User}", user);
|
||||
}
|
||||
else
|
||||
{
|
||||
identity.Label = ExternalLoginStatus.UserNotCreated;
|
||||
_logger.Log(alias.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add Identity User {Email} {Error}", email, result.Errors.ToString());
|
||||
_logger.Log(alias.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add User {Email}", email);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
identity.Label = ExternalLoginStatus.UserDoesNotExist;
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Creation Of New Users Is Disabled For This Site. User With Email Address {Email} Will First Need To Be Registered On The Site.", email);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var logins = await _identityUserManager.GetLoginsAsync(identityuser);
|
||||
var login = logins.FirstOrDefault(item => item.LoginProvider == (providerType + ":" + alias.SiteId.ToString()));
|
||||
if (login == null)
|
||||
{
|
||||
if (bool.Parse(httpContext.GetSiteSettings().GetValue("ExternalLogin:VerifyUsers", "true")))
|
||||
{
|
||||
// external login using existing user account - verification required
|
||||
var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>();
|
||||
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||
string url = httpContext.Request.Scheme + "://" + alias.Name;
|
||||
url += $"/login?name={identityuser.UserName}&token={WebUtility.UrlEncode(token)}&key={WebUtility.UrlEncode(id)}";
|
||||
string body = $"You Recently Signed In To Our Site With {providerName} Using The Email Address {email}. ";
|
||||
body += "In Order To Complete The Linkage Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(alias.SiteId, email, email, "External Login Linkage", body);
|
||||
_notifications.AddNotification(notification);
|
||||
|
||||
identity.Label = ExternalLoginStatus.VerificationRequired;
|
||||
_logger.Log(alias.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "External Login Linkage Verification For Provider {Provider} Sent To {Email}", providerName, email);
|
||||
}
|
||||
else
|
||||
{
|
||||
// external login using existing user account - link automatically
|
||||
user = _users.GetUser(identityuser.UserName);
|
||||
user.SiteId = alias.SiteId;
|
||||
|
||||
var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>();
|
||||
string url = httpContext.Request.Scheme + "://" + alias.Name;
|
||||
string body = "You Recently Used An External Account To Sign In To Our Site.\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, user, "User Account Notification", body);
|
||||
_notifications.AddNotification(notification);
|
||||
|
||||
// add user login
|
||||
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(providerType + ":" + user.SiteId.ToString(), id, providerName));
|
||||
|
||||
_logger.Log(user.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "External Login Linkage Created For User {Username} And Provider {Provider}", user.Username, providerName);
|
||||
identity.Label = ExternalLoginStatus.UserNotCreated;
|
||||
_logger.Log(alias.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add Identity User {Email} {Error}", email, result.Errors.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// provider keys do not match
|
||||
identity.Label = ExternalLoginStatus.ProviderKeyMismatch;
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Key Does Not Match For User {Username}. Login Denied.", identityuser.UserName);
|
||||
identity.Label = ExternalLoginStatus.UserDoesNotExist;
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Creation Of New Users Is Disabled For This Site. User With Email Address {Email} Will First Need To Be Registered On The Site.", email);
|
||||
}
|
||||
}
|
||||
}
|
||||
else // email invalid
|
||||
else
|
||||
{
|
||||
identity.Label = ExternalLoginStatus.InvalidEmail;
|
||||
if (!string.IsNullOrEmpty(email))
|
||||
var logins = await _identityUserManager.GetLoginsAsync(identityuser);
|
||||
var login = logins.FirstOrDefault(item => item.LoginProvider == (providerType + ":" + alias.SiteId.ToString()));
|
||||
if (login == null)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "The Email Address {Email} Is Invalid Or Does Not Match The Domain Filter Criteria. Login Denied.", email);
|
||||
if (bool.Parse(httpContext.GetSiteSettings().GetValue("ExternalLogin:VerifyUsers", "true")))
|
||||
{
|
||||
// external login using existing user account - verification required
|
||||
var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>();
|
||||
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||
string url = httpContext.Request.Scheme + "://" + alias.Name;
|
||||
url += $"/login?name={identityuser.UserName}&token={WebUtility.UrlEncode(token)}&key={WebUtility.UrlEncode(id)}";
|
||||
string body = $"You Recently Signed In To Our Site With {providerName} Using The Email Address {email}. ";
|
||||
body += "In Order To Complete The Linkage Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(alias.SiteId, email, email, "External Login Linkage", body);
|
||||
_notifications.AddNotification(notification);
|
||||
|
||||
identity.Label = ExternalLoginStatus.VerificationRequired;
|
||||
_logger.Log(alias.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "External Login Linkage Verification For Provider {Provider} Sent To {Email}", providerName, email);
|
||||
}
|
||||
else
|
||||
{
|
||||
// external login using existing user account - link automatically
|
||||
user = _users.GetUser(identityuser.UserName);
|
||||
user.SiteId = alias.SiteId;
|
||||
|
||||
var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>();
|
||||
string url = httpContext.Request.Scheme + "://" + alias.Name;
|
||||
string body = "You Recently Used An External Account To Sign In To Our Site.\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, user, "User Account Notification", body);
|
||||
_notifications.AddNotification(notification);
|
||||
|
||||
// add user login
|
||||
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(providerType + ":" + user.SiteId.ToString(), id, providerName));
|
||||
|
||||
_logger.Log(user.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "External Login Linkage Created For User {Username} And Provider {Provider}", user.Username, providerName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Did Not Return An Email Address To Uniquely Identify The User. The Email Claim Specified Was {EmailCLaimType} And Actual Claim Types Are {Claims}. Login Denied.", httpContext.GetSiteSettings().GetValue("ExternalLogin:EmailClaimType", ""), claims);
|
||||
// provider keys do not match
|
||||
identity.Label = ExternalLoginStatus.ProviderKeyMismatch;
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Key Does Not Match For User {Username}. Login Denied.", identityuser.UserName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -463,6 +536,25 @@ namespace Oqtane.Extensions
|
|||
user.LastIPAddress = httpContext.Connection.RemoteIpAddress.ToString();
|
||||
_users.UpdateUser(user);
|
||||
|
||||
// external roles
|
||||
if (!string.IsNullOrEmpty(httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimType", "")))
|
||||
{
|
||||
if (claimsPrincipal.Claims.Any(item => item.Type == ClaimTypes.Role))
|
||||
{
|
||||
foreach (var claim in claimsPrincipal.Claims.Where(item => item.Type == ClaimTypes.Role))
|
||||
{
|
||||
if (!identity.Claims.Any(item => item.Type == ClaimTypes.Role && item.Value == claim.Value))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.Role, claim.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "The Role Claim {ClaimType} Does Not Exist. Please Use The Review Claims Feature To View The Claims Returned By Your Provider.", httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimType", ""));
|
||||
}
|
||||
}
|
||||
|
||||
// user profile claims
|
||||
if (!string.IsNullOrEmpty(httpContext.GetSiteSettings().GetValue("ExternalLogin:ProfileClaimTypes", "")))
|
||||
{
|
||||
|
@ -501,7 +593,7 @@ namespace Oqtane.Extensions
|
|||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "The User Profile Claim {ClaimType} Does Not Exist. The Valid Claims Are {Claims}.", mapping.Split(":")[0], claims);
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "The User Profile Claim {ClaimType} Does Not Exist. Please Use The Review Claims Feature To View The Claims Returned By Your Provider.", mapping.Split(":")[0]);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -514,9 +606,10 @@ namespace Oqtane.Extensions
|
|||
_logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "External User Login Successful For {Username} Using Provider {Provider}", user.Username, providerName);
|
||||
}
|
||||
}
|
||||
else // id invalid
|
||||
else // claims invalid
|
||||
{
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Did Not Return An Identifier To Uniquely Identify The User. The Identifier Claim Specified Was {IdentifierCLaimType} And Actual Claim Types Are {Claims}. Login Denied.", httpContext.GetSiteSettings().GetValue("ExternalLogin:IdentifierClaimType", ""), claims);
|
||||
identity.Label = ExternalLoginStatus.MissingClaims;
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Did Not Return All Of The Claims Types Specified Or Email Address Does Not Saitisfy Domain Filter. The Actual Claims Returned Were {Claims}. Login Was Denied.", claims);
|
||||
}
|
||||
|
||||
return identity;
|
||||
|
|
|
@ -106,7 +106,7 @@ namespace Oqtane.Managers
|
|||
{
|
||||
if (string.IsNullOrEmpty(user.Password))
|
||||
{
|
||||
// create random interal password based on random date and punctuation ie. Jan-23-1981+14:43:12!
|
||||
// generate password based on random date and punctuation ie. Jan-23-1981+14:43:12!
|
||||
Random rnd = new Random();
|
||||
var date = DateTime.UtcNow.AddDays(-rnd.Next(50 * 365)).AddHours(rnd.Next(0, 24)).AddMinutes(rnd.Next(0, 60)).AddSeconds(rnd.Next(0, 60));
|
||||
user.Password = date.ToString("MMM-dd-yyyy+HH:mm:ss", CultureInfo.InvariantCulture) + (char)rnd.Next(33, 47);
|
||||
|
@ -152,7 +152,7 @@ namespace Oqtane.Managers
|
|||
{
|
||||
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||
string url = alias.Protocol + alias.Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
string body = "Dear " + user.DisplayName + ",\n\nIn Order To Complete The Registration Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
|
||||
string body = "Dear " + user.DisplayName + ",\n\nIn Order To Verify The Email Address Associated To Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, User, "User Account Verification", body);
|
||||
_notifications.AddNotification(notification);
|
||||
}
|
||||
|
@ -205,8 +205,22 @@ namespace Oqtane.Managers
|
|||
if (user.Email != identityuser.Email)
|
||||
{
|
||||
await _identityUserManager.SetEmailAsync(identityuser, user.Email);
|
||||
var emailConfirmationToken = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||
await _identityUserManager.ConfirmEmailAsync(identityuser, emailConfirmationToken);
|
||||
|
||||
// if email address changed and user is not administrator, email verification is required for new email address
|
||||
if (!user.EmailConfirmed)
|
||||
{
|
||||
var alias = _tenantManager.GetAlias();
|
||||
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||
string url = alias.Protocol + alias.Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
string body = "Dear " + user.DisplayName + ",\n\nIn Order To Verify The Email Address Associated To Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, user, "User Account Verification", body);
|
||||
_notifications.AddNotification(notification);
|
||||
}
|
||||
else
|
||||
{
|
||||
var emailConfirmationToken = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||
await _identityUserManager.ConfirmEmailAsync(identityuser, emailConfirmationToken);
|
||||
}
|
||||
}
|
||||
|
||||
user = _users.UpdateUser(user);
|
||||
|
@ -308,7 +322,7 @@ namespace Oqtane.Managers
|
|||
user = _users.GetUser(identityuser.UserName);
|
||||
if (user != null)
|
||||
{
|
||||
if (identityuser.EmailConfirmed)
|
||||
if (await _identityUserManager.IsEmailConfirmedAsync(identityuser))
|
||||
{
|
||||
user.IsAuthenticated = true;
|
||||
user.LastLoginOn = DateTime.UtcNow;
|
||||
|
@ -323,7 +337,7 @@ namespace Oqtane.Managers
|
|||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Not Verified {Username}", user.Username);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Email Address Not Verified {Username}", user.Username);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace Oqtane.Pages
|
|||
var providertype = HttpContext.GetSiteSettings().GetValue("ExternalLogin:ProviderType", "");
|
||||
if (providertype != "")
|
||||
{
|
||||
return new ChallengeResult(providertype, new AuthenticationProperties { RedirectUri = returnurl + (returnurl.Contains("?") ? "&" : "?") + "reload=post" });
|
||||
return new ChallengeResult(providertype, new AuthenticationProperties { RedirectUri = returnurl + (returnurl.Contains("?") ? "&" : "?") + "reload=post" });
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
namespace Oqtane.Shared {
|
||||
public class ExternalLoginStatus {
|
||||
public const string Success = "Success";
|
||||
public const string InvalidEmail = "InvalidEmail";
|
||||
public const string MissingClaims = "MissingClaims";
|
||||
public const string DuplicateEmail = "DuplicateEmail";
|
||||
public const string UserNotCreated = "UserNotCreated";
|
||||
public const string UserDoesNotExist = "UserDoesNotExist";
|
||||
|
|
Loading…
Reference in New Issue
Block a user