Files
sam-manage/app/Services/ApiTokenService.php

142 lines
4.2 KiB
PHP
Raw Normal View History

<?php
namespace App\Services;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
/**
* API 서버 토큰 교환 서비스
*
* MNG API 서버간 HMAC 인증을 통해 Bearer 토큰을 발급받습니다.
*/
class ApiTokenService
{
/**
* 서명 유효 시간 () - API 서버와 동일하게 5
*/
private const SIGNATURE_VALID_DURATION = 300;
/**
* API 토큰 교환
*
* @param int $userId 사용자 ID
* @param int $tenantId 테넌트 ID
* @return array{success: bool, data?: array, error?: string}
*/
public function exchangeToken(int $userId, int $tenantId): array
{
$baseUrl = config('services.api.base_url');
$exchangeSecret = config('services.api.exchange_secret');
if (empty($baseUrl)) {
Log::error('[ApiTokenService] API base URL not configured');
return ['success' => false, 'error' => 'API 서버 URL이 설정되지 않았습니다.'];
}
if (empty($exchangeSecret)) {
Log::error('[ApiTokenService] Exchange secret not configured');
return ['success' => false, 'error' => '토큰 교환 비밀키가 설정되지 않았습니다.'];
}
// 만료 시간 계산 (현재 시간 + 유효 시간)
$exp = time() + self::SIGNATURE_VALID_DURATION;
// HMAC 서명 생성
$payload = "{$userId}:{$tenantId}:{$exp}";
$signature = hash_hmac('sha256', $payload, $exchangeSecret);
try {
$response = Http::timeout(10)
->post("{$baseUrl}/api/v1/internal/exchange-token", [
'user_id' => $userId,
'tenant_id' => $tenantId,
'exp' => $exp,
'signature' => $signature,
]);
if ($response->successful()) {
$data = $response->json('data');
Log::info('[ApiTokenService] Token exchanged successfully', [
'user_id' => $userId,
'tenant_id' => $tenantId,
]);
return [
'success' => true,
'data' => [
'access_token' => $data['access_token'],
'token_type' => $data['token_type'] ?? 'Bearer',
'expires_in' => $data['expires_in'] ?? 3600,
],
];
}
$error = $response->json('message') ?? '토큰 교환에 실패했습니다.';
Log::warning('[ApiTokenService] Token exchange failed', [
'user_id' => $userId,
'tenant_id' => $tenantId,
'status' => $response->status(),
'error' => $error,
]);
return ['success' => false, 'error' => $error];
} catch (\Exception $e) {
Log::error('[ApiTokenService] Token exchange exception', [
'user_id' => $userId,
'tenant_id' => $tenantId,
'exception' => $e->getMessage(),
]);
return ['success' => false, 'error' => 'API 서버 연결에 실패했습니다.'];
}
}
/**
* 세션에 저장된 API 토큰 조회
*/
public function getSessionToken(): ?string
{
return session('api_access_token');
}
/**
* API 토큰을 세션에 저장
*
* @param string $token Bearer 토큰
* @param int $expiresIn 만료 시간 ()
*/
public function storeTokenInSession(string $token, int $expiresIn): void
{
session([
'api_access_token' => $token,
'api_token_expires_at' => now()->addSeconds($expiresIn)->timestamp,
]);
}
/**
* 세션의 API 토큰 삭제
*/
public function clearSessionToken(): void
{
session()->forget(['api_access_token', 'api_token_expires_at']);
}
/**
* 토큰이 만료되었는지 확인
*/
public function isTokenExpired(): bool
{
$expiresAt = session('api_token_expires_at');
if (! $expiresAt) {
return true;
}
// 5분 전에 미리 갱신하도록 (버퍼)
return time() > ($expiresAt - 300);
}
}