widimulcing

AuthService.php

Jul 29th, 2025 (edited)
243
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 4.05 KB | None | 0 0
  1. <?php
  2. //4o
  3. /**
  4.  * @file        AuthService.php
  5.  * @description Contains the core business logic for user authentication.
  6.  * It handles database interactions, password verification, and
  7.  * brute-force protection logic, independent of HTTP context.
  8.  *
  9.  * @version     1.0.0
  10.  * @since       2025-07-25
  11.  * @author      Barrac0de
  12.  */
  13. namespace App\Services;
  14.  
  15. use PDO;
  16.  
  17. class AuthService
  18. {
  19.     private PDO $pdo;
  20.  
  21.     public function __construct()
  22.     {
  23.         require __DIR__ . '/../config.php';
  24.         if (!isset($pdo) || !($pdo instanceof PDO)) {
  25.             http_response_code(500);
  26.             exit('Database connection failed.');
  27.         }
  28.         $this->pdo = $pdo;
  29.     }
  30.  
  31.     public function processLogin(): void
  32.     {
  33.         if (session_status() === PHP_SESSION_NONE) session_start();
  34.  
  35.         // Security headers
  36.         header('Strict-Transport-Security: max-age=31536000; includeSubDomains; preload');
  37.         header('X-Frame-Options: DENY');
  38.         header('X-Content-Type-Options: nosniff');
  39.         header('Referrer-Policy: no-referrer');
  40.         header("Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self';");
  41.  
  42.         // Session timeout check
  43.         $timeout = 3600;
  44.         if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity']) > $timeout) {
  45.             session_unset();
  46.             session_destroy();
  47.             header('Location: ' . BASE_PATH . 'login?timeout=1');
  48.             exit;
  49.         }
  50.         $_SESSION['last_activity'] = time();
  51.  
  52.         // CSRF check
  53.         $csrfToken = filter_input(INPUT_POST, 'csrf_token', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
  54.         if (empty($_SESSION['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $csrfToken)) {
  55.             $this->bounce('Invalid CSRF token.');
  56.         }
  57.  
  58.         // Input
  59.         $username = trim((string)filter_input(INPUT_POST, 'username', FILTER_SANITIZE_FULL_SPECIAL_CHARS));
  60.         $password = (string)filter_input(INPUT_POST, 'password', FILTER_UNSAFE_RAW);
  61.  
  62.         if ($username === '' || $password === '') {
  63.             $this->bounce('Username and password are required.');
  64.         }
  65.  
  66.         // Fetch user
  67.         $stmt = $this->pdo->prepare('SELECT id, password_hash, failed_login, last_failed_at FROM users WHERE username = :username LIMIT 1');
  68.         $stmt->execute([':username' => $username]);
  69.         $user = $stmt->fetch(PDO::FETCH_ASSOC);
  70.  
  71.         if (!$user) {
  72.             $this->bounce('Invalid username or password.');
  73.         }
  74.  
  75.         $failedLogin  = (int)$user['failed_login'];
  76.         $lastFailedAt = $user['last_failed_at'] ? strtotime($user['last_failed_at']) : 0;
  77.         $now          = time();
  78.         $maxAttempts  = 5;
  79.         $lockoutTime  = 24 * 60 * 60;
  80.  
  81.         if ($failedLogin >= $maxAttempts && ($now - $lastFailedAt) < $lockoutTime) {
  82.             $this->bounce('Account locked. Try again later.');
  83.         }
  84.  
  85.         // Password check
  86.         if (!password_verify($password, (string)$user['password_hash'])) {
  87.             $update = $this->pdo->prepare(
  88.                 'UPDATE users SET failed_login = failed_login + 1, last_failed_at = NOW() WHERE id = :id'
  89.             );
  90.             $update->execute([':id' => $user['id']]);
  91.             $this->bounce('Invalid username or password.');
  92.         }
  93.  
  94.         // Reset login counter
  95.         $reset = $this->pdo->prepare('UPDATE users SET failed_login = 0, last_failed_at = NULL WHERE id = :id');
  96.         $reset->execute([':id' => $user['id']]);
  97.  
  98.         // Login success
  99.         session_regenerate_id(true);
  100.         $_SESSION['user_id'] = $user['id'];
  101.         $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
  102.         header('Location: ' . BASE_PATH . 'index');
  103.         exit;
  104.     }
  105.  
  106.     private function bounce(string $message): void
  107.     {
  108.         $old = trim((string)filter_input(INPUT_POST, 'username', FILTER_SANITIZE_FULL_SPECIAL_CHARS));
  109.         $_SESSION['old_username'] = $old;
  110.         $_SESSION['login_error']  = $message;
  111.         header('Location: ' . BASE_PATH . 'login');
  112.         exit;
  113.     }
  114. }
  115.  
Advertisement
Add Comment
Please, Sign In to add comment