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