Handler.cs 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. using SharedKernel;
  2. using SharedKernel.Results;
  3. using Application.Abstractions.Authentication;
  4. using Application.Abstractions.Data;
  5. using Microsoft.EntityFrameworkCore;
  6. using Microsoft.Extensions.Options;
  7. using MediatR;
  8. namespace Application.Features.Api.Auth.Login;
  9. internal sealed class Handler(
  10. IAppDbContext db,
  11. IJwtTokenProvider jwtTokenProvider,
  12. IOptions<AppSettings> options
  13. ) : IRequestHandler<Command, Result<Response>> {
  14. private readonly AppSettings.JwtSection _jwt = options.Value.JWT;
  15. public async Task<Result<Response>> Handle(Command request, CancellationToken ct)
  16. {
  17. // 유효성 검사
  18. if (string.IsNullOrWhiteSpace(request.Email))
  19. {
  20. return Result.Failure<Response>(Error.Problem("Auth.EmailRequired", "이메일은 필수입니다."));
  21. }
  22. if (string.IsNullOrWhiteSpace(request.Password))
  23. {
  24. return Result.Failure<Response>(Error.Problem("Auth.PasswordRequired", "비밀번호는 필수입니다."));
  25. }
  26. // Member 조회 (비밀번호 검증을 위해 Tracking 모드)
  27. var email = request.Email.Trim().ToLower();
  28. var member = await db.Member.FirstOrDefaultAsync(m => m.Email == email, ct);
  29. if (member is null)
  30. {
  31. return Result.Failure<Response>(Error.Unauthorized("Auth.InvalidCredentials", "이메일 또는 비밀번호가 올바르지 않습니다."));
  32. }
  33. // 비밀번호 검증
  34. if (!member.VerifyPassword(request.Password))
  35. {
  36. return Result.Failure<Response>(Error.Unauthorized("Auth.InvalidCredentials", "이메일 또는 비밀번호가 올바르지 않습니다."));
  37. }
  38. // JWT 토큰 생성
  39. var accessToken = jwtTokenProvider.CreateAccessToken(member.ID, member.Email, member.Name);
  40. var refreshToken = jwtTokenProvider.CreateRefreshToken();
  41. var expiresAt = DateTime.UtcNow.AddMinutes(_jwt.AccessTokenExpiration);
  42. // RefreshToken 저장
  43. var refreshTokenEntity = Domain.Entities.Members.RefreshToken.Create(
  44. member.ID,
  45. refreshToken,
  46. DateTime.UtcNow.AddDays(_jwt.RefreshTokenExpiration)
  47. );
  48. await db.RefreshToken.AddAsync(refreshTokenEntity, ct);
  49. // 로그인 횟수 증가
  50. var memberStats = await db.MemberStats.FirstOrDefaultAsync(x => x.MemberID == member.ID, ct);
  51. if (memberStats is not null)
  52. {
  53. memberStats.LoginCount++;
  54. }
  55. await db.SaveChangesAsync(ct);
  56. return Result.Success(new Response(accessToken, refreshToken, expiresAt));
  57. }
  58. }