using System.Security.Claims; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.RateLimiting; using GameServer.Models; using GameServer.Models.Portfolio; using GameServer.Services; namespace GameServer.Controllers.Portfolio { /// /// 포트폴리오 관리자 로그인 / 로그아웃 / 세션 확인 API. /// 게임 API(/myGame/*) 와는 완전히 분리된 /api/portfolio/auth 경로를 사용한다. /// [ApiController] [Route("api/portfolio/auth")] public class PortfolioAuthController : ControllerBase { private readonly PortfolioAdminAuth _auth; public PortfolioAuthController(PortfolioAdminAuth auth) { _auth = auth; } /// 비밀번호로 로그인하고 관리자 쿠키를 발급한다. [HttpPost("login")] [EnableRateLimiting("portfolio-login")] public async Task Login([FromBody] LoginRequestDto request) { // 비밀번호가 아직 설정되지 않았다면 503 으로 안내한다. if (!_auth.IsConfigured) { return StatusCode(StatusCodes.Status503ServiceUnavailable, new ApiResponse { Data = new AuthStateDto { Authenticated = false }, Message = "관리자 비밀번호가 설정되지 않았습니다. appsettings.json의 Portfolio:AdminPassword 를 변경해 주세요." }); } if (!_auth.Verify(request?.Password)) { return StatusCode(StatusCodes.Status401Unauthorized, new ApiResponse { Data = new AuthStateDto { Authenticated = false }, Message = "비밀번호가 올바르지 않습니다." }); } // 관리자 신원 발급 (7일 유지) var claims = new List { new Claim(ClaimTypes.Name, "admin"), new Claim(ClaimTypes.Role, "admin") }; var identity = new ClaimsIdentity(claims, PortfolioAdminAuth.CookieScheme); var principal = new ClaimsPrincipal(identity); var props = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.AddDays(7) }; await HttpContext.SignInAsync(PortfolioAdminAuth.CookieScheme, principal, props); return Ok(new ApiResponse { Data = new AuthStateDto { Authenticated = true }, Message = "로그인되었습니다." }); } /// 관리자 쿠키를 만료시킨다. 언제 호출해도 200. [HttpPost("logout")] public async Task Logout() { await HttpContext.SignOutAsync(PortfolioAdminAuth.CookieScheme); return Ok(new ApiResponse { Data = new AuthStateDto { Authenticated = false }, Message = "로그아웃되었습니다." }); } /// /// 현재 로그인 상태를 알려준다. 절대 401 을 돌려주지 않는다. /// Message 에는 비밀번호 설정 여부("configured" / "not-configured")를 담아 /// 관리자 화면이 안내 문구를 띄울 수 있게 한다. /// [HttpGet("me")] public async Task Me() { var result = await HttpContext.AuthenticateAsync(PortfolioAdminAuth.CookieScheme); bool authenticated = result.Succeeded && result.Principal?.Identity?.IsAuthenticated == true; return Ok(new ApiResponse { Data = new AuthStateDto { Authenticated = authenticated }, Message = _auth.IsConfigured ? "configured" : "not-configured" }); } } }