Merge pull request #5878 from sbwalker/dev
refactor new Forgot Username and Login Link methods
This commit is contained in:
@ -243,6 +243,9 @@ else
|
|||||||
private void SetAction(string action)
|
private void SetAction(string action)
|
||||||
{
|
{
|
||||||
_action = action;
|
_action = action;
|
||||||
|
_username = "";
|
||||||
|
_password = "";
|
||||||
|
_email = "";
|
||||||
ClearModuleMessage();
|
ClearModuleMessage();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
@ -364,9 +367,7 @@ else
|
|||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(_username))
|
if (!string.IsNullOrEmpty(_username))
|
||||||
{
|
{
|
||||||
var user = new User { Username = _username };
|
if (await UserService.ForgotPasswordAsync(_username))
|
||||||
user = await UserService.ForgotPasswordAsync(user);
|
|
||||||
if (user != null)
|
|
||||||
{
|
{
|
||||||
await logger.LogInformation(LogFunction.Security, "Password Reset Notification Sent For Username {Username}", _username);
|
await logger.LogInformation(LogFunction.Security, "Password Reset Notification Sent For Username {Username}", _username);
|
||||||
AddModuleMessage(Localizer["Message.ForgotPassword"], MessageType.Info);
|
AddModuleMessage(Localizer["Message.ForgotPassword"], MessageType.Info);
|
||||||
@ -394,9 +395,7 @@ else
|
|||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(_email))
|
if (!string.IsNullOrEmpty(_email))
|
||||||
{
|
{
|
||||||
var user = new User { Email = _email };
|
if (await UserService.ForgotUsernameAsync(_email))
|
||||||
user = await UserService.ForgotUsernameAsync(user);
|
|
||||||
if (user != null)
|
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Message.ForgotUsername"], MessageType.Info);
|
AddModuleMessage(Localizer["Message.ForgotUsername"], MessageType.Info);
|
||||||
await logger.LogInformation(LogFunction.Security, "Username Reminder Notification Sent For Email {Email}", _email);
|
await logger.LogInformation(LogFunction.Security, "Username Reminder Notification Sent For Email {Email}", _email);
|
||||||
@ -424,9 +423,7 @@ else
|
|||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(_email))
|
if (!string.IsNullOrEmpty(_email))
|
||||||
{
|
{
|
||||||
var user = new User { Email = _email };
|
if (await UserService.SendLoginLinkAsync(_email))
|
||||||
user = await UserService.SendLoginLinkAsync(user);
|
|
||||||
if (user != null)
|
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Message.SendLoginLink"], MessageType.Info);
|
AddModuleMessage(Localizer["Message.SendLoginLink"], MessageType.Info);
|
||||||
await logger.LogInformation(LogFunction.Security, "Login Link Sent To Email {Email}", _email);
|
await logger.LogInformation(LogFunction.Security, "Login Link Sent To Email {Email}", _email);
|
||||||
|
|||||||
@ -97,18 +97,18 @@ namespace Oqtane.Services
|
|||||||
Task<User> VerifyEmailAsync(User user, string token);
|
Task<User> VerifyEmailAsync(User user, string token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Trigger a forgot-password e-mail for this <see cref="User"/>.
|
/// Trigger a forgot-password e-mail.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="user"></param>
|
/// <param name="username"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<User> ForgotPasswordAsync(User user);
|
Task<bool> ForgotPasswordAsync(string username);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Trigger a forgot-username e-mail for this <see cref="User"/>.
|
/// Trigger a username reminder e-mail.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="user"></param>
|
/// <param name="email"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<User> ForgotUsernameAsync(User user);
|
Task<bool> ForgotUsernameAsync(string email);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reset the password of this <see cref="User"/>
|
/// Reset the password of this <see cref="User"/>
|
||||||
@ -222,9 +222,9 @@ namespace Oqtane.Services
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Send a login link
|
/// Send a login link
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="user"></param>
|
/// <param name="email"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<User> SendLoginLinkAsync(User user);
|
Task<bool> SendLoginLinkAsync(string email);
|
||||||
}
|
}
|
||||||
|
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
@ -289,14 +289,14 @@ namespace Oqtane.Services
|
|||||||
return await PostJsonAsync<User>($"{Apiurl}/verify?token={token}", user);
|
return await PostJsonAsync<User>($"{Apiurl}/verify?token={token}", user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<User> ForgotPasswordAsync(User user)
|
public async Task<bool> ForgotPasswordAsync(string username)
|
||||||
{
|
{
|
||||||
return await PostJsonAsync<User>($"{Apiurl}/forgot", user);
|
return await GetJsonAsync<bool>($"{Apiurl}/forgotpassword?name={username}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<User> ForgotUsernameAsync(User user)
|
public async Task<bool> ForgotUsernameAsync(string email)
|
||||||
{
|
{
|
||||||
return await PostJsonAsync<User>($"{Apiurl}/forgotusername", user);
|
return await GetJsonAsync<bool>($"{Apiurl}/forgotusername?email={email}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<User> ResetPasswordAsync(User user, string token)
|
public async Task<User> ResetPasswordAsync(User user, string token)
|
||||||
@ -386,9 +386,9 @@ namespace Oqtane.Services
|
|||||||
await DeleteAsync($"{Apiurl}/login?id={userId}&provider={provider}&key={key}");
|
await DeleteAsync($"{Apiurl}/login?id={userId}&provider={provider}&key={key}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<User> SendLoginLinkAsync(User user)
|
public async Task<bool> SendLoginLinkAsync(string email)
|
||||||
{
|
{
|
||||||
return await PostJsonAsync<User>($"{Apiurl}/loginlink", user);
|
return await GetJsonAsync<bool>($"{Apiurl}/loginlink?email={email}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -294,26 +294,18 @@ namespace Oqtane.Controllers
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST api/<controller>/forgot
|
// GET api/<controller>/forgotpassword?name=x
|
||||||
[HttpPost("forgot")]
|
[HttpGet("forgotpassword")]
|
||||||
public async Task<User> Forgot([FromBody] User user)
|
public async Task<bool> ForgotPassword(string name)
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid)
|
return await _userManager.ForgotPassword(name);
|
||||||
{
|
|
||||||
return await _userManager.ForgotPassword(user);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST api/<controller>/forgotusername
|
// GET api/<controller>/forgotusername?email=x
|
||||||
[HttpPost("forgotusername")]
|
[HttpGet("forgotusername")]
|
||||||
public async Task<User> ForgotUsername([FromBody] User user)
|
public async Task<bool> ForgotUsername(string email)
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid)
|
return await _userManager.ForgotUsername(email);
|
||||||
{
|
|
||||||
return await _userManager.ForgotUsername(user);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST api/<controller>/reset
|
// POST api/<controller>/reset
|
||||||
@ -572,15 +564,11 @@ namespace Oqtane.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST api/<controller>/loginlink
|
// GET api/<controller>/loginlink?email=x
|
||||||
[HttpPost("loginlink")]
|
[HttpGet("loginlink")]
|
||||||
public async Task<User> SendLoginLink([FromBody] User user)
|
public async Task<bool> SendLoginLink(string email)
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid)
|
return await _userManager.SendLoginLink(email);
|
||||||
{
|
|
||||||
return await _userManager.SendLoginLink(user);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,8 +28,8 @@ namespace Oqtane.Managers
|
|||||||
Task<User> LoginUser(User user, bool setCookie, bool isPersistent);
|
Task<User> LoginUser(User user, bool setCookie, bool isPersistent);
|
||||||
Task LogoutUserEverywhere(User user);
|
Task LogoutUserEverywhere(User user);
|
||||||
Task<User> VerifyEmail(User user, string token);
|
Task<User> VerifyEmail(User user, string token);
|
||||||
Task<User> ForgotPassword(User user);
|
Task<bool> ForgotPassword(string username);
|
||||||
Task<User> ForgotUsername(User user);
|
Task<bool> ForgotUsername(string email);
|
||||||
Task<User> ResetPassword(User user, string token);
|
Task<User> ResetPassword(User user, string token);
|
||||||
User VerifyTwoFactor(User user, string token);
|
User VerifyTwoFactor(User user, string token);
|
||||||
Task<UserValidateResult> ValidateUser(string username, string email, string password);
|
Task<UserValidateResult> ValidateUser(string username, string email, string password);
|
||||||
@ -41,7 +41,7 @@ namespace Oqtane.Managers
|
|||||||
Task<List<UserLogin>> GetLogins(int userId, int siteId);
|
Task<List<UserLogin>> GetLogins(int userId, int siteId);
|
||||||
Task<User> AddLogin(User user, string token, string type, string key, string name);
|
Task<User> AddLogin(User user, string token, string type, string key, string name);
|
||||||
Task DeleteLogin(int userId, string provider, string key);
|
Task DeleteLogin(int userId, string provider, string key);
|
||||||
Task<User> SendLoginLink(User user);
|
Task<bool> SendLoginLink(string email);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UserManager : IUserManager
|
public class UserManager : IUserManager
|
||||||
@ -520,70 +520,72 @@ namespace Oqtane.Managers
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<User> ForgotPassword(User user)
|
public async Task<bool> ForgotPassword(string username)
|
||||||
{
|
{
|
||||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
if (!string.IsNullOrEmpty(username))
|
||||||
if (identityuser != null)
|
|
||||||
{
|
{
|
||||||
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
|
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(username);
|
||||||
|
|
||||||
var alias = _tenantManager.GetAlias();
|
|
||||||
user = GetUser(user.Username, alias.SiteId);
|
|
||||||
string url = alias.Protocol + alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
|
||||||
string siteName = _sites.GetSite(alias.SiteId).Name;
|
|
||||||
string subject = _localizer["ForgotPasswordEmailSubject"];
|
|
||||||
subject = subject.Replace("[SiteName]", siteName);
|
|
||||||
string body = _localizer["ForgotPasswordEmailBody"].Value;
|
|
||||||
body = body.Replace("[UserDisplayName]", user.DisplayName);
|
|
||||||
body = body.Replace("[URL]", url);
|
|
||||||
body = body.Replace("[SiteName]", siteName);
|
|
||||||
var notification = new Notification(_tenantManager.GetAlias().SiteId, user, subject, body);
|
|
||||||
_notifications.AddNotification(notification);
|
|
||||||
|
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset Notification Sent For {Username}", user.Username);
|
|
||||||
return new User { UserId = user.UserId, Username = user.Username, Email = user.Email }; // minimal object
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Notification Failed For {Username}", user.Username);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<User> ForgotUsername(User user)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
IdentityUser identityuser = await _identityUserManager.FindByEmailAsync(user.Email);
|
|
||||||
if (identityuser != null)
|
if (identityuser != null)
|
||||||
{
|
{
|
||||||
|
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
|
||||||
|
|
||||||
var alias = _tenantManager.GetAlias();
|
var alias = _tenantManager.GetAlias();
|
||||||
user = GetUser(identityuser.UserName, alias.SiteId);
|
var user = GetUser(username, alias.SiteId);
|
||||||
string url = alias.Protocol + alias.Name + "/login?name=" + user.Username;
|
string url = alias.Protocol + alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||||
string siteName = _sites.GetSite(alias.SiteId).Name;
|
string siteName = _sites.GetSite(alias.SiteId).Name;
|
||||||
string subject = _localizer["ForgotUsernameEmailSubject"];
|
string subject = _localizer["ForgotPasswordEmailSubject"];
|
||||||
subject = subject.Replace("[SiteName]", siteName);
|
subject = subject.Replace("[SiteName]", siteName);
|
||||||
string body = _localizer["ForgotUsernameEmailBody"].Value;
|
string body = _localizer["ForgotPasswordEmailBody"].Value;
|
||||||
body = body.Replace("[UserDisplayName]", user.DisplayName);
|
body = body.Replace("[UserDisplayName]", user.DisplayName);
|
||||||
body = body.Replace("[URL]", url);
|
body = body.Replace("[URL]", url);
|
||||||
body = body.Replace("[SiteName]", siteName);
|
body = body.Replace("[SiteName]", siteName);
|
||||||
var notification = new Notification(_tenantManager.GetAlias().SiteId, user, subject, body);
|
var notification = new Notification(_tenantManager.GetAlias().SiteId, user, subject, body);
|
||||||
_notifications.AddNotification(notification);
|
_notifications.AddNotification(notification);
|
||||||
|
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Forgot Username Notification Sent For {Email}", user.Email);
|
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset Notification Sent For {Username}", user.Username);
|
||||||
return new User { UserId = user.UserId, Username = user.Username, Email = user.Email }; // minimal object
|
return true;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Forgot Username Notification Failed For {Email}", user.Email);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
|
||||||
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Notification Failed For {Username}", username);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> ForgotUsername(string email)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(email))
|
||||||
|
{
|
||||||
|
IdentityUser identityuser = await _identityUserManager.FindByEmailAsync(email);
|
||||||
|
if (identityuser != null)
|
||||||
|
{
|
||||||
|
var alias = _tenantManager.GetAlias();
|
||||||
|
var user = GetUser(identityuser.UserName, alias.SiteId);
|
||||||
|
string url = alias.Protocol + alias.Name + "/login?name=" + user.Username;
|
||||||
|
string siteName = _sites.GetSite(alias.SiteId).Name;
|
||||||
|
string subject = _localizer["ForgotUsernameEmailSubject"];
|
||||||
|
subject = subject.Replace("[SiteName]", siteName);
|
||||||
|
string body = _localizer["ForgotUsernameEmailBody"].Value;
|
||||||
|
body = body.Replace("[UserDisplayName]", user.DisplayName);
|
||||||
|
body = body.Replace("[URL]", url);
|
||||||
|
body = body.Replace("[SiteName]", siteName);
|
||||||
|
var notification = new Notification(_tenantManager.GetAlias().SiteId, user, subject, body);
|
||||||
|
_notifications.AddNotification(notification);
|
||||||
|
|
||||||
|
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Forgot Username Notification Sent For {Email}", user.Email);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Forgot Username Notification Failed For {Email}", email);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// email may not be unique
|
// email may not be unique
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Forgot Username Notification Failed For {Email}", user.Email);
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, ex, "Forgot Username Notification Failed For {Email}", email);
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -958,48 +960,48 @@ namespace Oqtane.Managers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<User> SendLoginLink(User user)
|
public async Task<bool> SendLoginLink(string email)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
IdentityUser identityuser = await _identityUserManager.FindByEmailAsync(user.Email);
|
if (!string.IsNullOrEmpty(email))
|
||||||
if (identityuser != null)
|
|
||||||
{
|
{
|
||||||
var token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
IdentityUser identityuser = await _identityUserManager.FindByEmailAsync(email);
|
||||||
|
if (identityuser != null)
|
||||||
|
{
|
||||||
|
var token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||||
|
|
||||||
var alias = _tenantManager.GetAlias();
|
var alias = _tenantManager.GetAlias();
|
||||||
user = GetUser(identityuser.UserName, alias.SiteId);
|
var user = GetUser(identityuser.UserName, alias.SiteId);
|
||||||
user.TwoFactorCode = token;
|
user.TwoFactorCode = token;
|
||||||
user.TwoFactorExpiry = DateTime.UtcNow.AddMinutes(10);
|
user.TwoFactorExpiry = DateTime.UtcNow.AddMinutes(10);
|
||||||
_users.UpdateUser(user);
|
_users.UpdateUser(user);
|
||||||
|
|
||||||
string url = alias.Protocol + alias.Name + "/pages/loginlink?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
string url = alias.Protocol + alias.Name + "/pages/loginlink?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||||
string siteName = _sites.GetSite(alias.SiteId).Name;
|
string siteName = _sites.GetSite(alias.SiteId).Name;
|
||||||
string subject = _localizer["LoginLinkEmailSubject"];
|
string subject = _localizer["LoginLinkEmailSubject"];
|
||||||
subject = subject.Replace("[SiteName]", siteName);
|
subject = subject.Replace("[SiteName]", siteName);
|
||||||
string body = _localizer["LoginLinkEmailBody"].Value;
|
string body = _localizer["LoginLinkEmailBody"].Value;
|
||||||
body = body.Replace("[UserDisplayName]", user.DisplayName);
|
body = body.Replace("[UserDisplayName]", user.DisplayName);
|
||||||
body = body.Replace("[URL]", url);
|
body = body.Replace("[URL]", url);
|
||||||
body = body.Replace("[SiteName]", siteName);
|
body = body.Replace("[SiteName]", siteName);
|
||||||
var notification = new Notification(_tenantManager.GetAlias().SiteId, user, subject, body);
|
var notification = new Notification(_tenantManager.GetAlias().SiteId, user, subject, body);
|
||||||
_notifications.AddNotification(notification);
|
_notifications.AddNotification(notification);
|
||||||
|
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Login Link Notification Sent To {Email}", user.Email);
|
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Login Link Notification Sent To {Email}", user.Email);
|
||||||
return new User { UserId = user.UserId, Username = user.Username, Email = user.Email }; // minimal object
|
return true; // minimal object
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Login Link Notification Failed For {Email}", user.Email);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Login Link Notification Failed For {Email}", email);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// email may not be unique
|
// email may not be unique
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Login Link Notification Failed For {Email}", user.Email);
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, ex, "Login Link Notification Failed For {Email}", email);
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user