diff --git a/app/Core/Application.php b/app/Core/Application.php
index c6f7ace..f2e516a 100644
--- a/app/Core/Application.php
+++ b/app/Core/Application.php
@@ -64,7 +64,7 @@ final class Application
header('Strict-Transport-Security: max-age=31536000; includeSubDomains; preload');
header('Referrer-Policy: strict-origin-when-cross-origin');
header('Permissions-Policy: camera=(), microphone=(), geolocation=()');
- header('Content-Security-Policy: default-src \'self\'; script-src \'self\' cdn.tailwindcss.com unpkg.com; style-src \'self\' \'unsafe-inline\' fonts.googleapis.com; font-src fonts.gstatic.com');
+ header('Content-Security-Policy: default-src \'self\'; script-src \'self\' \'unsafe-inline\' cdn.tailwindcss.com unpkg.com cdn.jsdelivr.net; style-src \'self\' \'unsafe-inline\' fonts.googleapis.com; font-src fonts.gstatic.com; img-src \'self\' data: blob:;');
header_remove('X-Powered-By');
try {
diff --git a/app/Modules/Auth/AuthController.php b/app/Modules/Auth/AuthController.php
index 533fba6..bfd7fc8 100644
--- a/app/Modules/Auth/AuthController.php
+++ b/app/Modules/Auth/AuthController.php
@@ -178,6 +178,37 @@ final class AuthController
}
}
+ public function login2FAVerify(Request $request): void
+ {
+ $data = $request->getBody();
+ $code = $data['code'] ?? '';
+ $userId = $request->user->user_id;
+
+ $db = \App\Core\Database::getInstance();
+ $stmt = $db->prepare("SELECT totp_secret FROM users WHERE id = ?");
+ $stmt->execute([$userId]);
+ $secret = $stmt->fetchColumn();
+
+ $totpService = new \App\Services\TotpService();
+ if ($secret && $totpService->verify($secret, $code)) {
+ // Re-fetch user for full data
+ $stmt = $db->prepare("SELECT * FROM users WHERE id = ?");
+ $stmt->execute([$userId]);
+ $user = $stmt->fetch();
+
+ $authService = new AuthService();
+ $tokens = $authService->generateTokens($user);
+
+ Response::json([
+ 'success' => true,
+ 'data' => $tokens,
+ 'message' => 'تم التحقق بنجاح'
+ ]);
+ } else {
+ Response::error('رمز التحقق غير صحيح', 'INVALID_CODE', 401);
+ }
+ }
+
public function disable2FA(Request $request): void
{
$db = \App\Core\Database::getInstance();
diff --git a/public/index.php b/public/index.php
index 03a75c1..146ceae 100644
--- a/public/index.php
+++ b/public/index.php
@@ -31,6 +31,10 @@ $router->addRoute('POST', '/api/v1/auth/2fa/verify', [
'middleware' => [\App\Middleware\AuthMiddleware::class],
'handler' => [AuthController::class, 'verify2FA']
]);
+$router->addRoute('POST', '/api/v1/auth/2fa/verify_login', [
+ 'middleware' => [\App\Middleware\AuthMiddleware::class],
+ 'handler' => [AuthController::class, 'login2FAVerify']
+]);
$router->addRoute('POST', '/api/v1/auth/2fa/disable', [
'middleware' => [\App\Middleware\AuthMiddleware::class],
'handler' => [AuthController::class, 'disable2FA']
@@ -139,9 +143,10 @@ $router->addRoute('GET', '/api/v1/health', function($request) {
});
// ══ Determine if this is an API request ═════════════════════════════
-$apiRoute = $_GET['route'] ?? null;
+$requestPath = $_GET['route'] ?? parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
+$isApi = str_starts_with($requestPath, '/api/v1');
-if (!$apiRoute) {
+if (!$isApi) {
// Not an API call — serve the SPA shell
include __DIR__ . '/shell.php';
exit;
diff --git a/public/shell.php b/public/shell.php
index d76aea5..477971a 100644
--- a/public/shell.php
+++ b/public/shell.php
@@ -147,7 +147,7 @@