93 lines
3.7 KiB
C#
93 lines
3.7 KiB
C#
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
|
|
User? 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
|
|
string token = await userManager.GeneratePasswordResetTokenAsync(user);
|
|
|
|
// URL encode the token as it may contain characters that are not URL safe
|
|
string encodedToken = HttpUtility.UrlEncode(token);
|
|
|
|
// Build reset link
|
|
string resetLink =
|
|
$"{options.Value.FrontendBaseUrl}/reset-password?email={HttpUtility.UrlEncode(request.Email)}&token={encodedToken}";
|
|
|
|
// Create a styled email message
|
|
string subject = "Reset your Hutopy password";
|
|
string message = $"""
|
|
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; color: #333;">
|
|
<h1 style="color: #2c3e50; margin-bottom: 20px;">Reset Your Hutopy Password</h1>
|
|
|
|
<p style="font-size: 16px; line-height: 1.5; margin-bottom: 25px;">
|
|
Please click the button below to reset your password:
|
|
</p>
|
|
|
|
<div style="text-align: center; margin: 30px 0;">
|
|
<a href='{resetLink}'
|
|
style="background-color: #3498db;
|
|
color: white;
|
|
text-decoration: none;
|
|
padding: 12px 24px;
|
|
border-radius: 4px;
|
|
font-weight: bold;
|
|
display: inline-block;
|
|
box-shadow: 0 2px 5px rgba(0,0,0,0.1);">
|
|
Reset Password
|
|
</a>
|
|
</div>
|
|
|
|
<p style="font-size: 14px; color: #7f8c8d; margin-top: 30px;">
|
|
If you did not request a password reset, please ignore this email.
|
|
</p>
|
|
|
|
<p style="font-size: 14px; color: #7f8c8d; margin-top: 20px;">
|
|
If the button doesn't work, you can copy and paste this link into your browser:
|
|
<br>
|
|
<a href='{resetLink}' style="color: #3498db; word-break: break-all;">{resetLink}</a>
|
|
</p>
|
|
</div>
|
|
""";
|
|
|
|
// Send email
|
|
await emailSender.SendEmailAsync(request.Email, subject, message);
|
|
|
|
await SendOkAsync(ct);
|
|
}
|
|
}
|