diff --git a/Oqtane.Client/Modules/Admin/Login/Index.razor b/Oqtane.Client/Modules/Admin/Login/Index.razor
index f05dc1d3..80d4f5e4 100644
--- a/Oqtane.Client/Modules/Admin/Login/Index.razor
+++ b/Oqtane.Client/Modules/Admin/Login/Index.razor
@@ -93,45 +93,53 @@
protected override async Task OnInitializedAsync()
{
- _togglepassword = Localizer["ShowPassword"];
-
- if (PageState.Site.Settings.ContainsKey("LoginOptions:AllowSiteLogin") && !string.IsNullOrEmpty(PageState.Site.Settings["LoginOptions:AllowSiteLogin"]))
+ try
{
- _allowsitelogin = bool.Parse(PageState.Site.Settings["LoginOptions:AllowSiteLogin"]);
- }
+ _togglepassword = Localizer["ShowPassword"];
- if (PageState.Site.Settings.ContainsKey("ExternalLogin:ProviderType") && !string.IsNullOrEmpty(PageState.Site.Settings["ExternalLogin:ProviderType"]))
- {
- _allowexternallogin = true;
- }
-
- if (PageState.QueryString.ContainsKey("returnurl"))
- {
- _returnUrl = PageState.QueryString["returnurl"];
- }
-
- if (PageState.QueryString.ContainsKey("name"))
- {
- _username = PageState.QueryString["name"];
- }
-
- if (PageState.QueryString.ContainsKey("token"))
- {
- var user = new User();
- user.SiteId = PageState.Site.SiteId;
- user.Username = _username;
- user = await UserService.VerifyEmailAsync(user, PageState.QueryString["token"]);
-
- if (user != null)
+ if (PageState.Site.Settings.ContainsKey("LoginOptions:AllowSiteLogin") && !string.IsNullOrEmpty(PageState.Site.Settings["LoginOptions:AllowSiteLogin"]))
{
- await logger.LogInformation(LogFunction.Security, "Email Verified For For Username {Username}", _username);
- AddModuleMessage(Localizer["Success.Account.Verified"], MessageType.Info);
+ _allowsitelogin = bool.Parse(PageState.Site.Settings["LoginOptions:AllowSiteLogin"]);
}
- else
+
+ if (PageState.Site.Settings.ContainsKey("ExternalLogin:ProviderType") && !string.IsNullOrEmpty(PageState.Site.Settings["ExternalLogin:ProviderType"]))
{
- await logger.LogError(LogFunction.Security, "Email Verification Failed For Username {Username}", _username);
- AddModuleMessage(Localizer["Message.Account.NotVerfied"], MessageType.Warning);
+ _allowexternallogin = true;
}
+
+ if (PageState.QueryString.ContainsKey("returnurl"))
+ {
+ _returnUrl = PageState.QueryString["returnurl"];
+ }
+
+ if (PageState.QueryString.ContainsKey("name"))
+ {
+ _username = PageState.QueryString["name"];
+ }
+
+ if (PageState.QueryString.ContainsKey("token"))
+ {
+ var user = new User();
+ user.SiteId = PageState.Site.SiteId;
+ user.Username = _username;
+ user = await UserService.VerifyEmailAsync(user, PageState.QueryString["token"]);
+
+ if (user != null)
+ {
+ await logger.LogInformation(LogFunction.Security, "Email Verified For For Username {Username}", _username);
+ AddModuleMessage(Localizer["Success.Account.Verified"], MessageType.Info);
+ }
+ else
+ {
+ await logger.LogError(LogFunction.Security, "Email Verification Failed For Username {Username}", _username);
+ AddModuleMessage(Localizer["Message.Account.NotVerfied"], MessageType.Warning);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Loading Login {Error}", ex.Message);
+ AddModuleMessage(Localizer["Error.LoadLogin"], MessageType.Error);
}
}
@@ -145,65 +153,73 @@
private async Task Login()
{
- validated = true;
- var interop = new Interop(JSRuntime);
- if (await interop.FormValid(login))
+ try
{
- var user = new User { SiteId = PageState.Site.SiteId, Username = _username, Password = _password};
-
- if (!twofactor)
+ validated = true;
+ var interop = new Interop(JSRuntime);
+ if (await interop.FormValid(login))
{
- user = await UserService.LoginUserAsync(user, false, false);
- }
- else
- {
- user = await UserService.VerifyTwoFactorAsync(user, _code);
- }
+ var user = new User { SiteId = PageState.Site.SiteId, Username = _username, Password = _password};
- if (user.IsAuthenticated)
- {
- await logger.LogInformation(LogFunction.Security, "Login Successful For Username {Username}", _username);
-
- if (PageState.Runtime == Oqtane.Shared.Runtime.Server)
+ if (!twofactor)
{
- // server-side Blazor needs to post to the Login page so that the cookies are set correctly
- var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
- string url = Utilities.TenantUrl(PageState.Alias, "/pages/login/");
- await interop.SubmitForm(url, fields);
+ user = await UserService.LoginUserAsync(user, false, false);
}
else
{
- var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
- authstateprovider.NotifyAuthenticationChanged();
- NavigationManager.NavigateTo(NavigateUrl(_returnUrl, true));
+ user = await UserService.VerifyTwoFactorAsync(user, _code);
}
- }
- else
- {
- if (user.TwoFactorRequired)
+
+ if (user.IsAuthenticated)
{
- twofactor = true;
- validated = false;
- AddModuleMessage(Localizer["Message.TwoFactor"], MessageType.Info);
- }
- else
- {
- if (!twofactor)
+ await logger.LogInformation(LogFunction.Security, "Login Successful For Username {Username}", _username);
+
+ if (PageState.Runtime == Oqtane.Shared.Runtime.Server)
{
- await logger.LogInformation(LogFunction.Security, "Login Failed For Username {Username}", _username);
- AddModuleMessage(Localizer["Error.Login.Fail"], MessageType.Error);
+ // server-side Blazor needs to post to the Login page so that the cookies are set correctly
+ var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
+ string url = Utilities.TenantUrl(PageState.Alias, "/pages/login/");
+ await interop.SubmitForm(url, fields);
}
else
{
- await logger.LogInformation(LogFunction.Security, "Two Factor Verification Failed For Username {Username}", _username);
- AddModuleMessage(Localizer["Error.TwoFactor.Fail"], MessageType.Error);
+ var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
+ authstateprovider.NotifyAuthenticationChanged();
+ NavigationManager.NavigateTo(NavigateUrl(_returnUrl, true));
+ }
+ }
+ else
+ {
+ if (user.TwoFactorRequired)
+ {
+ twofactor = true;
+ validated = false;
+ AddModuleMessage(Localizer["Message.TwoFactor"], MessageType.Info);
+ }
+ else
+ {
+ if (!twofactor)
+ {
+ await logger.LogInformation(LogFunction.Security, "Login Failed For Username {Username}", _username);
+ AddModuleMessage(Localizer["Error.Login.Fail"], MessageType.Error);
+ }
+ else
+ {
+ await logger.LogInformation(LogFunction.Security, "Two Factor Verification Failed For Username {Username}", _username);
+ AddModuleMessage(Localizer["Error.TwoFactor.Fail"], MessageType.Error);
+ }
}
}
}
+ else
+ {
+ AddModuleMessage(Localizer["Message.Required.UserInfo"], MessageType.Warning);
+ }
}
- else
+ catch (Exception ex)
{
- AddModuleMessage(Localizer["Message.Required.UserInfo"], MessageType.Warning);
+ await logger.LogError(ex, "Error Performing Login {Error}", ex.Message);
+ AddModuleMessage(Localizer["Error.Login"], MessageType.Error);
}
}
@@ -214,26 +230,34 @@
private async Task Forgot()
{
- if (_username != string.Empty)
+ try
{
- var user = await UserService.GetUserAsync(_username, PageState.Site.SiteId);
- if (user != null)
+ if (_username != string.Empty)
{
- await UserService.ForgotPasswordAsync(user);
- await logger.LogInformation(LogFunction.Security, "Password Reset Notification Sent For Username {Username}", _username);
- AddModuleMessage(Localizer["Message.ForgotUser"], MessageType.Info);
+ var user = await UserService.GetUserAsync(_username, PageState.Site.SiteId);
+ if (user != null)
+ {
+ await UserService.ForgotPasswordAsync(user);
+ await logger.LogInformation(LogFunction.Security, "Password Reset Notification Sent For Username {Username}", _username);
+ AddModuleMessage(Localizer["Message.ForgotUser"], MessageType.Info);
+ }
+ else
+ {
+ AddModuleMessage(Localizer["Message.UserDoesNotExist"], MessageType.Warning);
+ }
}
else
{
- AddModuleMessage(Localizer["Message.UserDoesNotExist"], MessageType.Warning);
+ AddModuleMessage(Localizer["Message.ForgotPassword"], MessageType.Info);
}
- }
- else
- {
- AddModuleMessage(Localizer["Message.ForgotPassword"], MessageType.Info);
- }
- StateHasChanged();
+ StateHasChanged();
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Resetting Password {Error}", ex.Message);
+ AddModuleMessage(Localizer["Error.ResetPassword"], MessageType.Error);
+ }
}
private void Reset()
diff --git a/Oqtane.Client/Modules/Admin/Logs/Detail.razor b/Oqtane.Client/Modules/Admin/Logs/Detail.razor
index 7111572e..1953a72d 100644
--- a/Oqtane.Client/Modules/Admin/Logs/Detail.razor
+++ b/Oqtane.Client/Modules/Admin/Logs/Detail.razor
@@ -155,7 +155,7 @@
}
}
- if (log.PageId != null && log.ModuleId != null)
+ if (log.PageId != null && log.ModuleId != null && log.ModuleId != -1)
{
var pagemodule = await PageModuleService.GetPageModuleAsync(log.PageId.Value, log.ModuleId.Value);
if (pagemodule != null)
diff --git a/Oqtane.Client/Resources/Modules/Admin/Login/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Login/Index.resx
index 0760ce56..33c9fb60 100644
--- a/Oqtane.Client/Resources/Modules/Admin/Login/Index.resx
+++ b/Oqtane.Client/Resources/Modules/Admin/Login/Index.resx
@@ -192,4 +192,13 @@
Use
+
+ Error Loading Login
+
+
+ Error Performing Login
+
+
+ Error Resetting Password
+
\ No newline at end of file
diff --git a/Oqtane.Client/Themes/Controls/Theme/LoginBase.cs b/Oqtane.Client/Themes/Controls/Theme/LoginBase.cs
index 1d456565..b7a3646f 100644
--- a/Oqtane.Client/Themes/Controls/Theme/LoginBase.cs
+++ b/Oqtane.Client/Themes/Controls/Theme/LoginBase.cs
@@ -34,7 +34,7 @@ namespace Oqtane.Themes.Controls
protected async Task LogoutUser()
{
await UserService.LogoutUserAsync(PageState.User);
- await LoggingService.Log(PageState.Alias, PageState.Page.PageId, PageState.ModuleId, PageState.User.UserId, GetType().AssemblyQualifiedName, "Logout", LogFunction.Security, LogLevel.Information, null, "User Logout For Username {Username}", PageState.User.Username);
+ await LoggingService.Log(PageState.Alias, PageState.Page.PageId, null, PageState.User.UserId, GetType().AssemblyQualifiedName, "Logout", LogFunction.Security, LogLevel.Information, null, "User Logout For Username {Username}", PageState.User.Username);
PageState.User = null;
var url = PageState.Alias.Path + "/" + PageState.Page.Path;
diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs
index f34c1ce1..7377abfa 100644
--- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs
+++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs
@@ -184,7 +184,7 @@ namespace Microsoft.Extensions.DependencyInjection
options.SignIn.RequireConfirmedPhoneNumber = false;
// User settings
- options.User.RequireUniqueEmail = true;
+ options.User.RequireUniqueEmail = false; // changing to true will cause issues for legacy data
options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
});
diff --git a/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs b/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs
index 0e1f525d..ce34632f 100644
--- a/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs
+++ b/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs
@@ -199,56 +199,73 @@ namespace Oqtane.Extensions
}
User user = null;
- var identityuser = await _identityUserManager.FindByEmailAsync(email);
+ bool duplicates = false;
+ IdentityUser identityuser = null;
+ try
+ {
+ identityuser = await _identityUserManager.FindByEmailAsync(email);
+ }
+ catch
+ {
+ // FindByEmailAsync will throw an error if the email matches multiple user accounts
+ duplicates = true;
+ }
if (identityuser == null)
{
- if (bool.Parse(httpContext.GetSiteSettings().GetValue("ExternalLogin:CreateUsers", "true")))
+ if (duplicates)
{
- 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"));
- if (result.Succeeded)
+ _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")))
{
- user = new User
+ 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"));
+ if (result.Succeeded)
{
- SiteId = httpContext.GetAlias().SiteId,
- Username = email,
- DisplayName = email,
- Email = email,
- LastLoginOn = null,
- LastIPAddress = ""
- };
- user = _users.AddUser(user);
+ user = new User
+ {
+ SiteId = httpContext.GetAlias().SiteId,
+ Username = email,
+ DisplayName = email,
+ Email = email,
+ LastLoginOn = null,
+ LastIPAddress = ""
+ };
+ user = _users.AddUser(user);
- if (user != null)
- {
- var _notifications = httpContext.RequestServices.GetRequiredService();
- string url = httpContext.Request.Scheme + "://" + httpContext.GetAlias().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);
+ if (user != null)
+ {
+ var _notifications = httpContext.RequestServices.GetRequiredService();
+ string url = httpContext.Request.Scheme + "://" + httpContext.GetAlias().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, providerKey, ""));
+ // add user login
+ await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(providerType, providerKey, ""));
- _logger.Log(user.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "User Added {User}", user);
+ _logger.Log(user.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "User Added {User}", user);
+ }
+ else
+ {
+ _logger.Log(user.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add User {Email}", email);
+ }
}
else
{
- _logger.Log(user.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add User {Email}", email);
+ _logger.Log(user.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add Identity User {Email} {Error}", email, result.Errors.ToString());
}
}
else
{
- _logger.Log(user.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add Identity User {Email} {Error}", email, result.Errors.ToString());
+ _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
- {
- _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
{