66 lines
2.1 KiB
C#
66 lines
2.1 KiB
C#
using System.Text;
|
|
using System.Web;
|
|
using Hutopy.Infrastructure.Configuration;
|
|
using Hutopy.Infrastructure.Emailer.Contracts;
|
|
using Hutopy.Modules.Identity.Data;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace Hutopy.Modules.Identity.Handlers;
|
|
|
|
[PublicAPI]
|
|
public record ForgotPasswordRequest(
|
|
string Email);
|
|
|
|
[PublicAPI]
|
|
public class ForgotPasswordHandler(
|
|
UserManager userManager,
|
|
IEmailSender emailSender,
|
|
IOptionsSnapshot<WebsiteOptions> options)
|
|
: Endpoint<ForgotPasswordRequest>
|
|
{
|
|
public override void Configure()
|
|
{
|
|
AllowAnonymous();
|
|
Post("/api/users/forgot-password");
|
|
Options(o => o.WithTags("Users"));
|
|
}
|
|
|
|
public override async Task HandleAsync(
|
|
ForgotPasswordRequest request,
|
|
CancellationToken ct)
|
|
{
|
|
// Find user by email
|
|
var user = await userManager.FindByEmailAsync(request.Email);
|
|
|
|
// Always return OK even if user not found to prevent email enumeration
|
|
if (user is null)
|
|
{
|
|
await SendOkAsync(ct);
|
|
return;
|
|
}
|
|
|
|
// Generate password reset token
|
|
var token = await userManager.GeneratePasswordResetTokenAsync(user);
|
|
|
|
// URL encode the token as it may contain characters that are not URL safe
|
|
var encodedToken = HttpUtility.UrlEncode(token);
|
|
|
|
// Build reset link
|
|
var resetLink = $"{options.Value.FrontendBaseUrl}/reset-password?email={HttpUtility.UrlEncode(request.Email)}&token={encodedToken}";
|
|
|
|
// TODO: Write a better email template
|
|
var subject = "Reset Your Password";
|
|
var message = new StringBuilder()
|
|
.AppendLine("<h1>Reset Your Password</h1>")
|
|
.AppendLine("<p>Please click the link below to reset your password:</p>")
|
|
.AppendLine($"<p><a href=\"{resetLink}\">Reset Password</a></p>")
|
|
.AppendLine("<p>If you did not request a password reset, please ignore this email.</p>")
|
|
.ToString();
|
|
|
|
// Send email
|
|
await emailSender.SendEmailAsync(request.Email, subject, message);
|
|
|
|
await SendOkAsync(ct);
|
|
}
|
|
}
|