feat: API Key 재발급 API 구현 (#46) #47

Merged
seonkyu.kim merged 1 commits from feature/#46-api-key-refresh into develop 2026-02-09 15:16:23 +00:00
4 changed files with 58 additions and 0 deletions
Showing only changes of commit 4cb54e4c41 - Show all commits

View File

@ -63,4 +63,18 @@ public class ServiceController : ControllerBase
var result = await _serviceManagementService.ChangeStatusAsync(serviceCode, request);
return Ok(ApiResponse<ServiceResponseDto>.Success(result));
}
[HttpPost("{serviceCode}/apikey/refresh")]
[SwaggerOperation(
Summary = "API Key 재발급",
Description = "서비스의 API Key를 재발급합니다. 기존 키는 즉시 무효화되며, 새 키는 1회만 표시됩니다.")]
[SwaggerResponse(200, "재발급 성공", typeof(ApiResponse<ApiKeyRefreshResponseDto>))]
[SwaggerResponse(401, "인증되지 않은 요청")]
[SwaggerResponse(403, "권한 없음")]
[SwaggerResponse(404, "서비스를 찾을 수 없음")]
public async Task<IActionResult> RefreshApiKeyAsync([FromRoute] string serviceCode)
{
var result = await _serviceManagementService.RefreshApiKeyAsync(serviceCode);
return Ok(ApiResponse<ApiKeyRefreshResponseDto>.Success(result));
}
}

View File

@ -0,0 +1,8 @@
namespace SPMS.Application.DTOs.Service;
public class ApiKeyRefreshResponseDto
{
public string ServiceCode { get; set; } = string.Empty;
public string ApiKey { get; set; } = string.Empty;
public DateTime ApiKeyCreatedAt { get; set; }
}

View File

@ -7,4 +7,5 @@ public interface IServiceManagementService
Task<ServiceListResponseDto> GetListAsync(ServiceListRequestDto request);
Task<ServiceResponseDto> GetByServiceCodeAsync(string serviceCode);
Task<ServiceResponseDto> ChangeStatusAsync(string serviceCode, ChangeServiceStatusRequestDto request);
Task<ApiKeyRefreshResponseDto> RefreshApiKeyAsync(string serviceCode);
}

View File

@ -1,4 +1,5 @@
using System.Linq.Expressions;
using System.Security.Cryptography;
using SPMS.Application.DTOs.Service;
using SPMS.Application.Interfaces;
using SPMS.Domain.Common;
@ -110,6 +111,40 @@ public class ServiceManagementService : IServiceManagementService
return MapToDto(service);
}
public async Task<ApiKeyRefreshResponseDto> RefreshApiKeyAsync(string serviceCode)
{
var service = await _serviceRepository.GetByServiceCodeAsync(serviceCode);
if (service is null)
{
throw new SpmsException(
ErrorCodes.NotFound,
"서비스를 찾을 수 없습니다.",
404);
}
// 32~64자 랜덤 API Key 생성 (48자 = 36바이트 Base64)
var randomBytes = new byte[36];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(randomBytes);
}
var newApiKey = Convert.ToBase64String(randomBytes);
service.ApiKey = newApiKey;
service.ApiKeyCreatedAt = DateTime.UtcNow;
service.UpdatedAt = DateTime.UtcNow;
_serviceRepository.Update(service);
await _unitOfWork.SaveChangesAsync();
return new ApiKeyRefreshResponseDto
{
ServiceCode = service.ServiceCode,
ApiKey = newApiKey,
ApiKeyCreatedAt = service.ApiKeyCreatedAt
};
}
private static ServiceSummaryDto MapToSummaryDto(Service service)
{
return new ServiceSummaryDto