using System; using System.Security.Claims; using System.Security.Cryptography; using System.Net.Mail; using System.Text; using System.Threading.Tasks; using D3ROVoice.Api.Dtos; using D3ROVoice.Api.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.RateLimiting; namespace D3ROVoice.Api.Controllers; [ApiController] [Route("api/[controller]")] public class AuthController : ControllerBase { private readonly IAuthService _authService; private readonly IConfiguration _configuration; public AuthController(IAuthService authService, IConfiguration configuration) { _authService = authService; _configuration = configuration; } [HttpPost("register")] [EnableRateLimiting("auth")] [RequestSizeLimit(16 * 1024)] public async Task Register( [FromBody] RegisterDto dto, [FromHeader(Name = "X-D3RO-Bootstrap-Token")] string? bootstrapToken) { var normalizedEmail = dto.Email?.Trim().ToLowerInvariant() ?? string.Empty; if ( normalizedEmail.Length is < 3 or > 150 || !MailAddress.TryCreate(normalizedEmail, out var parsedEmail) || !string.Equals(parsedEmail.Address, normalizedEmail, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(dto.Password) ) { return BadRequest(new { message = "유효한 이메일과 비밀번호를 입력해주세요." }); } var configuredToken = _configuration["ADMIN_BOOTSTRAP_TOKEN"]; if ( string.IsNullOrWhiteSpace(configuredToken) || Encoding.UTF8.GetByteCount(configuredToken) < 32 ) { return StatusCode(503, new { message = "관리자 초기 등록이 비활성화되어 있습니다." }); } var configuredDigest = SHA256.HashData(Encoding.UTF8.GetBytes(configuredToken)); var providedDigest = SHA256.HashData(Encoding.UTF8.GetBytes(bootstrapToken ?? string.Empty)); if (!CryptographicOperations.FixedTimeEquals(configuredDigest, providedDigest)) { return Unauthorized(new { message = "관리자 초기 등록 토큰이 올바르지 않습니다." }); } try { var result = await _authService.RegisterAsync(dto); return Ok(result); } catch (InvalidOperationException ex) { return Conflict(new { message = ex.Message }); } } [HttpPost("login")] [EnableRateLimiting("auth")] [RequestSizeLimit(16 * 1024)] public async Task Login([FromBody] LoginDto dto) { var normalizedEmail = dto.Email?.Trim().ToLowerInvariant() ?? string.Empty; if ( normalizedEmail.Length is < 3 or > 150 || !MailAddress.TryCreate(normalizedEmail, out _) || string.IsNullOrWhiteSpace(dto.Password) || dto.Password.Length > 256 ) { return BadRequest(new { message = "이메일과 비밀번호를 입력해주세요." }); } try { var result = await _authService.LoginAsync(dto); return Ok(result); } catch (UnauthorizedAccessException ex) { return Unauthorized(new { message = ex.Message }); } } [Authorize] [HttpGet("me")] public async Task GetMe() { var email = User.FindFirstValue(ClaimTypes.Email); if (string.IsNullOrEmpty(email)) return Unauthorized(); var user = await _authService.GetUserByEmailAsync(email); if (user == null) return NotFound(); return Ok(user); } }