Files
social-media/backend/Modules/Identity/Handlers/Login.cs

90 lines
2.5 KiB
C#

using Hutopy.Infrastructure.Security;
using Hutopy.Modules.Identity.Configuration;
using Hutopy.Modules.Identity.Data;
using Microsoft.Extensions.Options;
namespace Hutopy.Modules.Identity.Handlers;
[PublicAPI]
public record LoginRequest(
string Email,
string Password);
[PublicAPI]
public record LoginResponse(
string AccessToken,
string RefreshToken);
[PublicAPI]
public class LoginHandler(
UserManager userManager,
IOptionsSnapshot<JwtOptions> jwtOptions)
: Endpoint<LoginRequest, LoginResponse>
{
public override void Configure()
{
AllowAnonymous();
Post("/api/users/login");
Options(o => o.WithTags("Users"));
}
public override async Task HandleAsync(
LoginRequest request,
CancellationToken ct)
{
// Find the user by email
User? user = await userManager.FindByEmailAsync(request.Email);
if (user is null)
{
await SendStringAsync(
"Invalid email or password",
401,
cancellation: ct);
return;
}
// Verify password
bool isPasswordValid = await userManager.CheckPasswordAsync(user, request.Password);
if (!isPasswordValid)
{
await SendStringAsync(
"Invalid email or password",
401,
cancellation: ct);
return;
}
// Check if the email is confirmed
if (!user.EmailConfirmed)
{
await SendStringAsync(
"Email not verified. Please check your email for verification instructions.",
401,
cancellation: ct);
return;
}
// Generate a new refresh token
user.RefreshToken = RefreshTokenGenerator.Next();
user.RefreshTokenExpiryTime = DateTime.UtcNow.Add(jwtOptions.Value.RefreshTokenLifetime);
await userManager.UpdateAsync(user);
// Generate JWT token
string accessToken = JwtTokenHelper.GenerateJwtToken(
jwtOptions.Value.Lifetime,
jwtOptions.Value.Issuer,
jwtOptions.Value.Audience,
jwtOptions.Value.Key,
user.Id.ToString(),
user.Email ?? string.Empty,
user.Alias,
user.Firstname ?? string.Empty,
user.Lastname ?? string.Empty,
user.PortraitUrl);
await SendOkAsync(
new LoginResponse(accessToken, user.RefreshToken),
ct);
}
}