Implement password recovery feature with reset token and email notifications
All checks were successful
Build And Push Dev Docker Image / docker (push) Successful in 1m28s
All checks were successful
Build And Push Dev Docker Image / docker (push) Successful in 1m28s
This commit is contained in:
@@ -50,6 +50,11 @@ namespace FoodsharingSiegen.Server.Auth
|
||||
/// </summary>
|
||||
private User? _user;
|
||||
|
||||
/// <summary>
|
||||
/// The mail service
|
||||
/// </summary>
|
||||
private readonly IMailService _mailService;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Setup/Teardown
|
||||
@@ -60,14 +65,17 @@ namespace FoodsharingSiegen.Server.Auth
|
||||
/// <param name="context">The context</param>
|
||||
/// <param name="localStorageService">The local storage service</param>
|
||||
/// <param name="authenticationStateProvider">The authentication state provider</param>
|
||||
/// <param name="mailService">The mail service</param>
|
||||
public AuthService(
|
||||
FsContext context,
|
||||
LocalStorageService localStorageService,
|
||||
AuthenticationStateProvider authenticationStateProvider)
|
||||
AuthenticationStateProvider authenticationStateProvider,
|
||||
IMailService mailService)
|
||||
{
|
||||
Context = context;
|
||||
_localStorageService = localStorageService;
|
||||
_authenticationStateProvider = authenticationStateProvider;
|
||||
_mailService = mailService;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -186,5 +194,52 @@ namespace FoodsharingSiegen.Server.Auth
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Password Recovery
|
||||
|
||||
public async Task InitiatePasswordReset(string email, string baseUri)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(email)) return;
|
||||
|
||||
var user = await Context.Users!.FirstOrDefaultAsync(x => x.Mail.ToLower() == email.ToLower());
|
||||
if (user == null) return; // Do not leak existence
|
||||
|
||||
var resetToken = Guid.NewGuid().ToString("N");
|
||||
user.ResetToken = resetToken;
|
||||
user.ResetTokenExpiry = DateTime.UtcNow.AddMinutes(30);
|
||||
|
||||
await Context.SaveChangesAsync();
|
||||
|
||||
var resetLink = $"{baseUri.TrimEnd('/')}/reset-password/{resetToken}";
|
||||
var mailBody = $"Hallo {user.Name},<br><br>Um dein Passwort zurückzusetzen, klicke bitte auf den folgenden Link (dieser ist 30 Minuten gültig):<br><a href='{resetLink}'>{resetLink}</a><br><br>Viele Grüße<br>Dein Foodsharing Team";
|
||||
|
||||
await _mailService.SendEmailAsync(user.Mail, "Passwort zurücksetzen", mailBody);
|
||||
}
|
||||
|
||||
public async Task<OperationResult> ResetPassword(string token, string newPassword)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(token)) return new OperationResult(new Exception("Ungültiges Token."));
|
||||
if (string.IsNullOrWhiteSpace(newPassword)) return new OperationResult(new Exception("Passwort darf nicht leer sein."));
|
||||
|
||||
var user = await Context.Users!.FirstOrDefaultAsync(x => x.ResetToken == token && x.ResetTokenExpiry > DateTime.UtcNow);
|
||||
if (user == null) return new OperationResult(new Exception("Token ist ungültig oder abgelaufen."));
|
||||
|
||||
user.Password = newPassword;
|
||||
user.ResetToken = null;
|
||||
user.ResetTokenExpiry = null;
|
||||
|
||||
await Context.SaveChangesAsync();
|
||||
|
||||
return new OperationResult();
|
||||
}
|
||||
|
||||
public async Task<bool> IsResetTokenValid(string token)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(token)) return false;
|
||||
var user = await Context.Users!.FirstOrDefaultAsync(x => x.ResetToken == token && x.ResetTokenExpiry > DateTime.UtcNow);
|
||||
return user != null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user