140 lines
5.4 KiB
PHP
140 lines
5.4 KiB
PHP
<?php
|
|
namespace App\Controllers;
|
|
|
|
use App\Helpers\Response;
|
|
use App\Helpers\Security;
|
|
use App\Helpers\Validator;
|
|
use App\Middleware\RateLimitMiddleware;
|
|
use App\Models\User;
|
|
use App\Services\AuditLogger;
|
|
use App\Services\TwoFactorService;
|
|
|
|
/**
|
|
* Class AuthController
|
|
* Authentication REST API Controller (13-digit National ID, Argon2id, 2FA TOTP, JWT)
|
|
*
|
|
* @package App\Controllers
|
|
*/
|
|
class AuthController
|
|
{
|
|
/**
|
|
* POST /api/v1/auth/login
|
|
*/
|
|
public function login(): void
|
|
{
|
|
RateLimitMiddleware::handle(10, 60); // ป้องกัน Brute-force สูงสุด 10 ครั้ง/นาทีต่อ IP
|
|
|
|
$input = json_decode(file_get_contents('php://input'), true) ?? $_POST;
|
|
$validator = Validator::make($input, [
|
|
'national_id' => 'required|cid_13',
|
|
'password' => 'required|min:8',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
Response::error('ข้อมูลเข้าสู่ระบบไม่ถูกต้อง (Validation Failed)', 422, $validator->getErrors());
|
|
}
|
|
|
|
$userModel = new User();
|
|
$authRes = $userModel->verifyCredentials(trim($input['national_id']), $input['password']);
|
|
|
|
if (!$authRes['success']) {
|
|
AuditLogger::logLoginFailed(trim($input['national_id']), $authRes['error']);
|
|
Response::error($authRes['error'], 401);
|
|
}
|
|
|
|
$user = $authRes['user'];
|
|
|
|
// ตรวจสอบว่าบัญชีนี้เปิดใช้งาน 2FA หรือไม่
|
|
if ((int)$user['two_factor_enabled'] === 1) {
|
|
if (session_status() === PHP_SESSION_NONE) session_start();
|
|
$_SESSION['pending_2fa_user_id'] = (int)$user['id'];
|
|
$_SESSION['pending_2fa_user_role'] = $user['role'];
|
|
$_SESSION['2fa_verified'] = false;
|
|
|
|
Response::success('2FA Verification Required', [
|
|
'require_2fa' => true,
|
|
'user_id' => (int)$user['id'],
|
|
'message' => 'กรุณากรอกรหัส OTP 6 หลักจากแอป Google Authenticator',
|
|
], 200);
|
|
}
|
|
|
|
// หากไม่เปิด 2FA ให้สร้าง JWT Access Token ทันที
|
|
$this->issueTokenAndRespond($user);
|
|
}
|
|
|
|
/**
|
|
* POST /api/v1/auth/verify-2fa
|
|
*/
|
|
public function verifyTwoFactor(): void
|
|
{
|
|
RateLimitMiddleware::handle(5, 60); // สูงสุด 5 ครั้ง/นาทีสำหรับ 2FA
|
|
|
|
$input = json_decode(file_get_contents('php://input'), true) ?? $_POST;
|
|
if (empty($input['code']) || strlen($input['code']) !== 6) {
|
|
Response::error('กรุณาระบุรหัส TOTP 6 หลักที่ถูกต้อง', 422);
|
|
}
|
|
|
|
if (session_status() === PHP_SESSION_NONE) session_start();
|
|
$userId = $_SESSION['pending_2fa_user_id'] ?? ($input['user_id'] ?? null);
|
|
|
|
if (!$userId) {
|
|
Response::error('Session หมดอายุ กรุณาเข้าสู่ระบบใหม่อีกครั้ง', 401);
|
|
}
|
|
|
|
$userModel = new User();
|
|
$user = $userModel->find((int)$userId);
|
|
|
|
if (!$user || (int)$user['two_factor_enabled'] !== 1 || empty($user['two_factor_secret'])) {
|
|
Response::error('ไม่พบการตั้งค่า 2FA หรือผู้ใช้งานไม่ถูกต้อง', 400);
|
|
}
|
|
|
|
// Verify TOTP Code
|
|
if (!TwoFactorService::verifyCode($user['two_factor_secret'], trim($input['code']))) {
|
|
Response::error('รหัส TOTP ไม่ถูกต้องหรือหมดเวลา กรุณาลองใหม่อีกครั้ง', 401);
|
|
}
|
|
|
|
$_SESSION['2fa_verified'] = true;
|
|
unset($user['password_hash'], $user['two_factor_secret'], $user['two_factor_recovery_codes']);
|
|
|
|
$this->issueTokenAndRespond($user);
|
|
}
|
|
|
|
private function issueTokenAndRespond(array $user): void
|
|
{
|
|
$config = require __DIR__ . '/../../config/security.php';
|
|
$secret = $config['jwt']['secret'];
|
|
$ttl = $config['jwt']['access_token_ttl'];
|
|
|
|
$payload = [
|
|
'sub' => (int)$user['id'],
|
|
'nat_id' => $user['national_id'],
|
|
'role' => $user['role'],
|
|
'name' => $user['first_name'] . ' ' . $user['last_name'],
|
|
'branch' => (int)($user['branch_id'] ?? 1),
|
|
];
|
|
|
|
$token = Security::generateJwt($payload, $secret, $ttl);
|
|
|
|
if (session_status() === PHP_SESSION_NONE) session_start();
|
|
$_SESSION['access_token'] = $token;
|
|
$_SESSION['user_id'] = (int)$user['id'];
|
|
$_SESSION['user_role'] = $user['role'];
|
|
$_SESSION['branch_id'] = (int)($user['branch_id'] ?? 1);
|
|
|
|
AuditLogger::logLogin((int)$user['id'], $user['role']);
|
|
|
|
Response::success('เข้าสู่ระบบสำเร็จ (Authentication Successful)', [
|
|
'access_token' => $token,
|
|
'token_type' => 'Bearer',
|
|
'expires_in' => $ttl,
|
|
'user' => [
|
|
'id' => (int)$user['id'],
|
|
'national_id' => $user['national_id'],
|
|
'full_name' => $user['title'] . $user['first_name'] . ' ' . $user['last_name'],
|
|
'role' => $user['role'],
|
|
'branch_id' => (int)($user['branch_id'] ?? 1),
|
|
]
|
|
], 200);
|
|
}
|
|
}
|