1. /menus 페이지 hx-boost 네비게이션 시 SortableJS 미실행 수정 - htmx:afterSettle 이벤트로 페이지별 스크립트 초기화 - menu-sortable.js로 SortableJS 로직 분리 - 중복 코드 제거 2. 세션 만료 시 자동 갱신 로직 추가 - /auth/refresh-session 엔드포인트 추가 - Remember Token으로 자동 재인증 (자동 로그인 사용자) - 재인증 실패 시 로그인 페이지 리다이렉트
154 lines
4.6 KiB
PHP
154 lines
4.6 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Auth;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Requests\Auth\LoginRequest;
|
|
use App\Services\ApiTokenService;
|
|
use App\Services\AuthService;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\View\View;
|
|
|
|
class LoginController extends Controller
|
|
{
|
|
public function __construct(
|
|
private readonly AuthService $authService,
|
|
private readonly ApiTokenService $apiTokenService
|
|
) {}
|
|
|
|
/**
|
|
* 로그인 폼 표시
|
|
*/
|
|
public function showLoginForm(): View
|
|
{
|
|
return view('auth.login');
|
|
}
|
|
|
|
/**
|
|
* 로그인 처리
|
|
*/
|
|
public function login(LoginRequest $request): RedirectResponse
|
|
{
|
|
$credentials = $request->only('email', 'password');
|
|
$remember = $request->boolean('remember', false);
|
|
|
|
if ($this->authService->login($credentials, $remember)) {
|
|
$request->session()->regenerate();
|
|
|
|
return redirect()->intended('/dashboard')
|
|
->with('success', '로그인되었습니다.');
|
|
}
|
|
|
|
// AuthService에서 설정한 오류 메시지 사용
|
|
$errorMessage = $this->authService->getLoginError()
|
|
?? '이메일 또는 비밀번호가 올바르지 않습니다.';
|
|
|
|
return back()
|
|
->withErrors(['email' => $errorMessage])
|
|
->withInput($request->only('email'));
|
|
}
|
|
|
|
/**
|
|
* 로그아웃 처리
|
|
*/
|
|
public function logout(): RedirectResponse
|
|
{
|
|
$this->authService->logout();
|
|
|
|
request()->session()->invalidate();
|
|
request()->session()->regenerateToken();
|
|
|
|
return redirect('/login')
|
|
->with('success', '로그아웃되었습니다.');
|
|
}
|
|
|
|
/**
|
|
* 세션 갱신 (Remember Token으로 재인증)
|
|
* HTMX 401 에러 시 호출되어 세션을 갱신합니다.
|
|
*/
|
|
public function refreshSession(): JsonResponse
|
|
{
|
|
// 이미 인증된 경우 (세션이 아직 유효한 경우)
|
|
if (Auth::check()) {
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => '세션이 유효합니다.',
|
|
]);
|
|
}
|
|
|
|
// Remember Token으로 재인증 시도
|
|
// Laravel의 Auth::viaRemember()는 remember token 쿠키로 인증 시도
|
|
if (Auth::viaRemember()) {
|
|
$user = Auth::user();
|
|
|
|
// HQ 테넌트 소속 확인
|
|
if (! $user->belongsToHQ()) {
|
|
Auth::logout();
|
|
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => '본사 소속 직원만 접근할 수 있습니다.',
|
|
'redirect' => '/login',
|
|
], 401);
|
|
}
|
|
|
|
// 활성 상태 확인
|
|
if (! $user->is_active) {
|
|
Auth::logout();
|
|
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => '비활성화된 계정입니다.',
|
|
'redirect' => '/login',
|
|
], 401);
|
|
}
|
|
|
|
// HQ 테넌트를 기본 선택
|
|
$hqTenant = $user->getHQTenant();
|
|
if ($hqTenant) {
|
|
session(['selected_tenant_id' => $hqTenant->id]);
|
|
|
|
// API 토큰 재발급
|
|
$this->refreshApiToken($user->id, $hqTenant->id);
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => '세션이 갱신되었습니다.',
|
|
]);
|
|
}
|
|
|
|
// Remember Token이 없거나 유효하지 않은 경우
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => '세션이 만료되었습니다. 다시 로그인해주세요.',
|
|
'redirect' => '/login',
|
|
], 401);
|
|
}
|
|
|
|
/**
|
|
* API 토큰 재발급
|
|
*/
|
|
private function refreshApiToken(int $userId, int $tenantId): void
|
|
{
|
|
try {
|
|
$result = $this->apiTokenService->exchangeToken($userId, $tenantId);
|
|
|
|
if ($result['success']) {
|
|
$this->apiTokenService->storeTokenInSession(
|
|
$result['data']['access_token'],
|
|
$result['data']['expires_in']
|
|
);
|
|
}
|
|
} catch (\Exception $e) {
|
|
// API 토큰 재발급 실패해도 세션 갱신은 계속 진행
|
|
\Log::warning('[LoginController] API token refresh failed', [
|
|
'user_id' => $userId,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
}
|
|
}
|
|
}
|