111 lines
3.7 KiB
C#
111 lines
3.7 KiB
C#
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<IActionResult> 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<IActionResult> 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<IActionResult> 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);
|
|
}
|
|
}
|