using System; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using D3ROVoice.Api.Data; using D3ROVoice.Api.Dtos; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; namespace D3ROVoice.Api.Services; public interface IAuthService { Task RegisterAsync(RegisterDto dto); Task LoginAsync(LoginDto dto); Task GetUserByEmailAsync(string email); } public class AuthService : IAuthService { private readonly AppDbContext _db; private readonly IConfiguration _config; public AuthService(AppDbContext db, IConfiguration config) { _db = db; _config = config; } public async Task RegisterAsync(RegisterDto dto) { var existing = await _db.Users.FirstOrDefaultAsync(u => u.Email.ToLower() == dto.Email.ToLower()); if (existing != null) { throw new InvalidOperationException("이미 등록된 이메일 주소입니다."); } var isFirstUser = !await _db.Users.AnyAsync(); var user = new User { Email = dto.Email.Trim().ToLower(), PasswordHash = HashPassword(dto.Password), Role = isFirstUser ? "Admin" : "User", CreatedAt = DateTime.UtcNow, IsActive = true }; _db.Users.Add(user); await _db.SaveChangesAsync(); return GenerateToken(user); } public async Task LoginAsync(LoginDto dto) { var user = await _db.Users.FirstOrDefaultAsync(u => u.Email.ToLower() == dto.Email.ToLower()); if (user == null || !VerifyPassword(dto.Password, user.PasswordHash)) { throw new UnauthorizedAccessException("이메일 또는 비밀번호가 올바르지 않습니다."); } if (!user.IsActive) { throw new UnauthorizedAccessException("비활성화된 계정입니다. 관리자에게 문의하세요."); } user.LastLoginAt = DateTime.UtcNow; await _db.SaveChangesAsync(); return GenerateToken(user); } public async Task GetUserByEmailAsync(string email) { var user = await _db.Users.AsNoTracking().FirstOrDefaultAsync(u => u.Email.ToLower() == email.ToLower()); if (user == null) return null; return new UserInfoDto(user.Id, user.Email, user.Role, user.CreatedAt, user.LastLoginAt, user.IsActive); } private AuthResponseDto GenerateToken(User user) { var secretKey = _config["Jwt:SecretKey"] ?? "D3ROVoice_Super_Secure_Secret_Key_2026_Key!"; var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var claims = new[] { new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.Email, user.Email), new Claim(ClaimTypes.Role, user.Role) }; var expiresAt = DateTime.UtcNow.AddDays(30); var token = new JwtSecurityToken( issuer: _config["Jwt:Issuer"] ?? "D3ROVoiceApi", audience: _config["Jwt:Audience"] ?? "D3ROVoiceClient", claims: claims, expires: expiresAt, signingCredentials: creds ); var tokenHandler = new JwtSecurityTokenHandler(); return new AuthResponseDto(tokenHandler.WriteToken(token), user.Email, user.Role, expiresAt); } public static string HashPassword(string password) { using var sha256 = SHA256.Create(); var bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(password + "D3RO_SALT_2026")); return Convert.ToBase64String(bytes); } private static bool VerifyPassword(string password, string hash) { return HashPassword(password) == hash; } }