diff --git a/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs b/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs index 7c88d49f..b63b9ad3 100644 --- a/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs @@ -133,7 +133,7 @@ namespace Oqtane.Extensions output = "[" + output + "]"; // convert to json array } JsonNode items = JsonNode.Parse(output)!; - foreach(var item in items.AsArray()) + foreach (var item in items.AsArray()) { if (item[emailClaimType] != null) { @@ -235,97 +235,112 @@ namespace Oqtane.Extensions ClaimsIdentity identity = new ClaimsIdentity(Constants.AuthenticationScheme); // use identity.Label as a temporary location to store validation status information - if (EmailValid(email, httpContext.GetSiteSettings().GetValue("ExternalLogin:DomainFilter", ""))) + var providerType = httpContext.GetSiteSettings().GetValue("ExternalLogin:ProviderType", ""); + var providerName = httpContext.GetSiteSettings().GetValue("ExternalLogin:ProviderName", ""); + var alias = httpContext.GetAlias(); + var _users = httpContext.RequestServices.GetRequiredService(); + User user = null; + + // verify if external user is already registerd for this site + var _identityUserManager = httpContext.RequestServices.GetRequiredService>(); + var identityuser = await _identityUserManager.FindByLoginAsync(providerType + ":" + alias.SiteId.ToString(), id); + if (identityuser != null) { - var _identityUserManager = httpContext.RequestServices.GetRequiredService>(); - var _users = httpContext.RequestServices.GetRequiredService(); - var _userRoles = httpContext.RequestServices.GetRequiredService(); - var alias = httpContext.GetAlias(); - var providerType = httpContext.GetSiteSettings().GetValue("ExternalLogin:ProviderType", ""); - var providerName = httpContext.GetSiteSettings().GetValue("ExternalLogin:ProviderName", ""); - User user = null; - - bool duplicates = false; - IdentityUser identityuser = null; - try + user = _users.GetUser(identityuser.UserName); + } + else + { + if (EmailValid(email, httpContext.GetSiteSettings().GetValue("ExternalLogin:DomainFilter", ""))) { - identityuser = await _identityUserManager.FindByEmailAsync(email); - } - catch - { - // FindByEmailAsync will throw an error if the email matches multiple user accounts - duplicates = true; - } - if (identityuser == null) - { - if (duplicates) + bool duplicates = false; + try { - identity.Label = ExternalLoginStatus.DuplicateEmail; - _logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Multiple Users Exist With Email Address {Email}. Login Denied.", email); + identityuser = await _identityUserManager.FindByEmailAsync(email); } - else + catch { - if (bool.Parse(httpContext.GetSiteSettings().GetValue("ExternalLogin:CreateUsers", "true"))) + // FindByEmailAsync will throw an error if the email matches multiple user accounts + duplicates = true; + } + if (identityuser == null) + { + 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) + 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"))) { - 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 = alias.SiteId, - Username = email, - DisplayName = email, - Email = email, - LastLoginOn = null, - LastIPAddress = "" - }; - user = _users.AddUser(user); + user = new User + { + SiteId = alias.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 + "://" + 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); + if (user != null) + { + var _notifications = httpContext.RequestServices.GetRequiredService(); + 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, id, "")); + // add user login + await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(providerType + ":" + alias.SiteId.ToString(), id, providerName)); - _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 + { + identity.Label = ExternalLoginStatus.UserNotCreated; + _logger.Log(user.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add User {Email}", email); + } } else { identity.Label = ExternalLoginStatus.UserNotCreated; - _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 { - identity.Label = ExternalLoginStatus.UserNotCreated; - _logger.Log(user.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add Identity User {Email} {Error}", email, result.Errors.ToString()); + 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 - { - 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) + else { - if (login.ProviderKey == id) + var logins = await _identityUserManager.GetLoginsAsync(identityuser); + var login = logins.FirstOrDefault(item => item.LoginProvider == (providerType + ":" + alias.SiteId.ToString())); + if (login == null) { - user = _users.GetUser(identityuser.UserName); + // new external login using existing user account - verification required + var _notifications = httpContext.RequestServices.GetRequiredService(); + 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 { @@ -334,48 +349,36 @@ namespace Oqtane.Extensions _logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Key Does Not Match For User {Username}. Login Denied.", identityuser.UserName); } } + } + else // email invalid + { + identity.Label = ExternalLoginStatus.InvalidEmail; + if (!string.IsNullOrEmpty(email)) + { + _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); + } else { - // new external login using existing user account - verification required - var _notifications = httpContext.RequestServices.GetRequiredService(); - 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); + _logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Did Not Return An Email To Uniquely Identify The User."); } } - - // manage user - if (user != null) - { - // create claims identity - identity = UserSecurity.CreateClaimsIdentity(alias, user, _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList()); - identity.Label = ExternalLoginStatus.Success; - - // update user - user.LastLoginOn = DateTime.UtcNow; - user.LastIPAddress = httpContext.Connection.RemoteIpAddress.ToString(); - _users.UpdateUser(user); - _logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "External User Login Successful For {Username} Using Provider {Provider}", user.Username, providerName); - } } - else // email invalid + + // manage user + if (user != null) { - identity.Label = ExternalLoginStatus.InvalidEmail; - if (!string.IsNullOrEmpty(email)) - { - _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); - } - else - { - _logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Did Not Return An Email To Uniquely Identify The User."); - } + // create claims identity + var _userRoles = httpContext.RequestServices.GetRequiredService(); + identity = UserSecurity.CreateClaimsIdentity(alias, user, _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList()); + identity.Label = ExternalLoginStatus.Success; + + // update user + user.LastLoginOn = DateTime.UtcNow; + user.LastIPAddress = httpContext.Connection.RemoteIpAddress.ToString(); + _users.UpdateUser(user); + _logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "External User Login Successful For {Username} Using Provider {Provider}", user.Username, providerName); } + return identity; }