- auth_sensitive 명명 Rate Limit 정책 추가 (20회/15분/IP) - AuthController 3개 + PasswordController 2개 메서드에 EnableRateLimiting 적용 - 로그인 시도 제한 구현 (5회/15분, Redis 카운터, LoginAttemptExceeded 에러코드 활성화) - 비밀번호 찾기/임시 비밀번호 요청 제한 (3회/1시간, silent 반환) - AuthService 보안 이벤트 구조적 로깅 (ILogger 주입) - Swagger 429 응답 문서화 Closes #190
62 lines
2.4 KiB
C#
62 lines
2.4 KiB
C#
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.AspNetCore.RateLimiting;
|
|
using Swashbuckle.AspNetCore.Annotations;
|
|
using SPMS.Application.DTOs.Account;
|
|
using SPMS.Application.Interfaces;
|
|
using SPMS.Domain.Common;
|
|
|
|
namespace SPMS.API.Controllers;
|
|
|
|
[ApiController]
|
|
[Route("v1/in/account/password")]
|
|
[ApiExplorerSettings(GroupName = "account")]
|
|
[AllowAnonymous]
|
|
public class PasswordController : ControllerBase
|
|
{
|
|
private readonly IAuthService _authService;
|
|
|
|
public PasswordController(IAuthService authService)
|
|
{
|
|
_authService = authService;
|
|
}
|
|
|
|
[HttpPost("forgot")]
|
|
[EnableRateLimiting("auth_sensitive")]
|
|
[SwaggerOperation(
|
|
Summary = "비밀번호 찾기",
|
|
Description = "등록된 이메일로 비밀번호 재설정 토큰을 발송합니다. 보안을 위해 이메일 존재 여부와 관계없이 동일한 응답을 반환합니다.")]
|
|
[SwaggerResponse(200, "재설정 메일 발송 완료")]
|
|
[SwaggerResponse(400, "잘못된 요청")]
|
|
public async Task<IActionResult> ForgotPasswordAsync([FromBody] PasswordForgotRequestDto request)
|
|
{
|
|
await _authService.ForgotPasswordAsync(request);
|
|
return Ok(ApiResponse.Success());
|
|
}
|
|
|
|
[HttpPost("reset")]
|
|
[SwaggerOperation(
|
|
Summary = "비밀번호 재설정",
|
|
Description = "이메일로 받은 재설정 토큰과 새 비밀번호로 비밀번호를 재설정합니다.")]
|
|
[SwaggerResponse(200, "비밀번호 재설정 성공")]
|
|
[SwaggerResponse(400, "토큰 불일치 또는 만료")]
|
|
public async Task<IActionResult> ResetPasswordAsync([FromBody] PasswordResetRequestDto request)
|
|
{
|
|
await _authService.ResetPasswordAsync(request);
|
|
return Ok(ApiResponse.Success());
|
|
}
|
|
|
|
[HttpPost("temp")]
|
|
[EnableRateLimiting("auth_sensitive")]
|
|
[SwaggerOperation(
|
|
Summary = "임시 비밀번호 발급",
|
|
Description = "등록된 이메일로 임시 비밀번호를 발송합니다. 보안을 위해 이메일 존재 여부와 관계없이 동일한 응답을 반환합니다.")]
|
|
[SwaggerResponse(200, "임시 비밀번호 발송 완료")]
|
|
[SwaggerResponse(400, "잘못된 요청")]
|
|
public async Task<IActionResult> IssueTempPasswordAsync([FromBody] TempPasswordRequestDto request)
|
|
{
|
|
await _authService.IssueTempPasswordAsync(request);
|
|
return Ok(ApiResponse.Success());
|
|
}
|
|
}
|