improvement: 로그아웃 연동 완료 (#253) #254

Merged
seonkyu.kim merged 1 commits from improvement/#253-logout-integration into develop 2026-02-26 01:25:00 +00:00
4 changed files with 25 additions and 6 deletions

View File

@ -82,8 +82,9 @@ public class AuthController : ControllerBase
[Authorize]
[SwaggerOperation(
Summary = "로그아웃",
Description = "현재 로그인된 관리자의 토큰을 무효화합니다. Refresh Token은 DB에서 삭제되고, Access Token은 Redis 블랙리스트에 등록되어 즉시 사용 불가합니다.")]
[SwaggerResponse(200, "로그아웃 성공")]
Description = "현재 로그인된 관리자의 토큰을 무효화합니다. Refresh Token은 DB에서 삭제되고, Access Token은 Redis 블랙리스트에 등록되어 즉시 사용 불가합니다. " +
"설정/마이페이지/프로필 등 모든 화면에서 이 단일 API를 사용합니다. 응답의 redirect_to로 이동합니다.")]
[SwaggerResponse(200, "로그아웃 성공", typeof(ApiResponse<LogoutResponseDto>))]
[SwaggerResponse(401, "인증되지 않은 요청")]
public async Task<IActionResult> LogoutAsync()
{
@ -94,8 +95,8 @@ public class AuthController : ControllerBase
var accessToken = HttpContext.Request.Headers["Authorization"]
.ToString().Replace("Bearer ", "");
await _authService.LogoutAsync(adminId, accessToken);
return Ok(ApiResponse.Success());
var result = await _authService.LogoutAsync(adminId, accessToken);
return Ok(ApiResponse<LogoutResponseDto>.Success(result));
}
[HttpPost("email/verify")]

View File

@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
namespace SPMS.Application.DTOs.Auth;
public class LogoutResponseDto
{
[JsonPropertyName("logged_out")]
public bool LoggedOut { get; set; }
[JsonPropertyName("redirect_to")]
public string RedirectTo { get; set; } = "/login";
}

View File

@ -8,7 +8,7 @@ public interface IAuthService
Task<SignupResponseDto> SignupAsync(SignupRequestDto request);
Task<LoginResponseDto> LoginAsync(LoginRequestDto request);
Task<TokenRefreshResponseDto> RefreshTokenAsync(TokenRefreshRequestDto request);
Task LogoutAsync(long adminId, string accessToken);
Task<LogoutResponseDto> LogoutAsync(long adminId, string accessToken);
Task<ChangePasswordResponseDto> ChangePasswordAsync(long adminId, ChangePasswordRequestDto request);
Task<EmailCheckResponseDto> CheckEmailAsync(EmailCheckRequestDto request);
Task<EmailVerifyResponseDto> VerifyEmailAsync(EmailVerifyRequestDto request);

View File

@ -287,7 +287,7 @@ public class AuthService : IAuthService
};
}
public async Task LogoutAsync(long adminId, string accessToken)
public async Task<LogoutResponseDto> LogoutAsync(long adminId, string accessToken)
{
// 1. 관리자 조회
var admin = await _adminRepository.GetByIdAsync(adminId);
@ -313,6 +313,12 @@ public class AuthService : IAuthService
if (remaining > TimeSpan.Zero)
await _tokenStore.StoreAsync($"blacklist:{jti}", "revoked", remaining);
}
return new LogoutResponseDto
{
LoggedOut = true,
RedirectTo = "/login"
};
}
public async Task<ChangePasswordResponseDto> ChangePasswordAsync(long adminId, ChangePasswordRequestDto request)