Merge pull request #2155 from sbwalker/dev

external login improvements
This commit is contained in:
Shaun Walker
2022-04-25 20:05:03 -04:00
committed by GitHub

View File

@ -133,7 +133,7 @@ namespace Oqtane.Extensions
output = "[" + output + "]"; // convert to json array output = "[" + output + "]"; // convert to json array
} }
JsonNode items = JsonNode.Parse(output)!; JsonNode items = JsonNode.Parse(output)!;
foreach(var item in items.AsArray()) foreach (var item in items.AsArray())
{ {
if (item[emailClaimType] != null) if (item[emailClaimType] != null)
{ {
@ -235,97 +235,112 @@ namespace Oqtane.Extensions
ClaimsIdentity identity = new ClaimsIdentity(Constants.AuthenticationScheme); ClaimsIdentity identity = new ClaimsIdentity(Constants.AuthenticationScheme);
// use identity.Label as a temporary location to store validation status information // 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<IUserRepository>();
User user = null;
// verify if external user is already registerd for this site
var _identityUserManager = httpContext.RequestServices.GetRequiredService<UserManager<IdentityUser>>();
var identityuser = await _identityUserManager.FindByLoginAsync(providerType + ":" + alias.SiteId.ToString(), id);
if (identityuser != null)
{ {
var _identityUserManager = httpContext.RequestServices.GetRequiredService<UserManager<IdentityUser>>(); user = _users.GetUser(identityuser.UserName);
var _users = httpContext.RequestServices.GetRequiredService<IUserRepository>(); }
var _userRoles = httpContext.RequestServices.GetRequiredService<IUserRoleRepository>(); else
var alias = httpContext.GetAlias(); {
var providerType = httpContext.GetSiteSettings().GetValue("ExternalLogin:ProviderType", ""); if (EmailValid(email, httpContext.GetSiteSettings().GetValue("ExternalLogin:DomainFilter", "")))
var providerName = httpContext.GetSiteSettings().GetValue("ExternalLogin:ProviderName", "");
User user = null;
bool duplicates = false;
IdentityUser identityuser = null;
try
{ {
identityuser = await _identityUserManager.FindByEmailAsync(email); bool duplicates = false;
} try
catch
{
// FindByEmailAsync will throw an error if the email matches multiple user accounts
duplicates = true;
}
if (identityuser == null)
{
if (duplicates)
{ {
identity.Label = ExternalLoginStatus.DuplicateEmail; identityuser = await _identityUserManager.FindByEmailAsync(email);
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Multiple Users Exist With Email Address {Email}. Login Denied.", 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(); identity.Label = ExternalLoginStatus.DuplicateEmail;
identityuser.UserName = email; _logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Multiple Users Exist With Email Address {Email}. Login Denied.", email);
identityuser.Email = email; }
identityuser.EmailConfirmed = true; else
var result = await _identityUserManager.CreateAsync(identityuser, DateTime.UtcNow.ToString("yyyy-MMM-dd-HH-mm-ss")); {
if (result.Succeeded) 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, user = new User
Username = email, {
DisplayName = email, SiteId = alias.SiteId,
Email = email, Username = email,
LastLoginOn = null, DisplayName = email,
LastIPAddress = "" Email = email,
}; LastLoginOn = null,
user = _users.AddUser(user); LastIPAddress = ""
};
user = _users.AddUser(user);
if (user != null) if (user != null)
{ {
var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>(); var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>();
string url = httpContext.Request.Scheme + "://" + alias.Name; 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!"; 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); var notification = new Notification(user.SiteId, user, "User Account Notification", body);
_notifications.AddNotification(notification); _notifications.AddNotification(notification);
// add user login // add user login
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(providerType, id, "")); 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 else
{ {
identity.Label = ExternalLoginStatus.UserNotCreated; 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 else
{ {
identity.Label = ExternalLoginStatus.UserNotCreated; identity.Label = ExternalLoginStatus.UserDoesNotExist;
_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
{
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
else
{
var logins = await _identityUserManager.GetLoginsAsync(identityuser);
var login = logins.FirstOrDefault(item => item.LoginProvider == (providerType + ":" + alias.SiteId.ToString()));
if (login != null)
{ {
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<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 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); _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 else
{ {
// new external login using existing user account - verification required _logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Did Not Return An Email To Uniquely Identify The User.");
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);
} }
} }
// 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; // create claims identity
if (!string.IsNullOrEmpty(email)) var _userRoles = httpContext.RequestServices.GetRequiredService<IUserRoleRepository>();
{ identity = UserSecurity.CreateClaimsIdentity(alias, user, _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList());
_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); identity.Label = ExternalLoginStatus.Success;
}
else // update user
{ user.LastLoginOn = DateTime.UtcNow;
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Did Not Return An Email To Uniquely Identify The User."); 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; return identity;
} }