widimulcing

AuthService.php

Jul 29th, 2025
247
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 3.80 KB | None | 0 0
  1. <?php
  2. // Claude
  3. /**
  4.  * @file        AuthService.php
  5.  * @description Service class for authentication operations including user verification,
  6.  *              password validation, and brute-force protection.
  7.  *
  8.  * @version     1.0.0
  9.  * @since       2025-07-29
  10.  * @author      Barrac0de
  11.  */
  12. declare(strict_types=1);
  13.  
  14. namespace App\Auth;
  15.  
  16. use PDO;
  17.  
  18. class AuthService
  19. {
  20.     /** @var PDO */
  21.     private $pdo;
  22.     /** @var int */
  23.     private $maxAttempts = 5;
  24.     /** @var int */
  25.     private $lockDuration = 86400; // 24 hours in seconds
  26.  
  27.     /**
  28.      * Constructor
  29.      *
  30.      * @param PDO $pdo Database connection
  31.      */
  32.     public function __construct(PDO $pdo)
  33.     {
  34.         $this->pdo = $pdo;
  35.     }
  36.  
  37.     /**
  38.      * Verify user credentials and handle login attempts
  39.      *
  40.      * @param string $username The username to verify
  41.      * @param string $password The password to verify
  42.      * @return array|false User data if authentication successful, false otherwise
  43.      */
  44.     public function authenticate(string $username, string $password)
  45.     {
  46.         if ($username === '' || $password === '') {
  47.             $this->bounce('Username and password are required.');
  48.             return false;
  49.         }
  50.  
  51.         // Fetch user record
  52.         $stmt = $this->pdo->prepare('SELECT id, password_hash, failed_login, last_failed_at FROM users WHERE username = :username LIMIT 1');
  53.         $stmt->execute([':username' => $username]);
  54.         $user = $stmt->fetch(PDO::FETCH_ASSOC);
  55.  
  56.         if (!$user) {
  57.             $this->bounce('Invalid username or password.');
  58.             return false;
  59.         }
  60.  
  61.         // Check for account lockout
  62.         $failedLogin = (int)$user['failed_login'];
  63.         $lastFailedAt = $user['last_failed_at'] ? strtotime($user['last_failed_at']) : 0;
  64.         $now = time();
  65.  
  66.         if ($failedLogin >= $this->maxAttempts && ($now - $lastFailedAt) < $this->lockDuration) {
  67.             $this->bounce('Account locked. Try again later.');
  68.             return false;
  69.         }
  70.  
  71.         // Verify password
  72.         if (!password_verify($password, (string)$user['password_hash'])) {
  73.             $this->incrementFailedLogin($user['id']);
  74.             $this->bounce('Invalid username or password.');
  75.             return false;
  76.         }
  77.  
  78.         // Reset failed login counter on successful login
  79.         $this->resetFailedLogin($user['id']);
  80.        
  81.         return $user;
  82.     }
  83.  
  84.     /**
  85.      * Increment the failed login counter for a user
  86.      *
  87.      * @param int $userId The user ID
  88.      * @return bool Success status
  89.      */
  90.     private function incrementFailedLogin(int $userId): bool
  91.     {
  92.         $update = $this->pdo->prepare(
  93.             'UPDATE users
  94.             SET failed_login = failed_login + 1, last_failed_at = NOW()
  95.             WHERE id = :id'
  96.         );
  97.         return $update->execute([':id' => $userId]);
  98.     }
  99.  
  100.     /**
  101.      * Reset the failed login counter for a user
  102.      *
  103.      * @param int $userId The user ID
  104.      * @return bool Success status
  105.      */
  106.     private function resetFailedLogin(int $userId): bool
  107.     {
  108.         $reset = $this->pdo->prepare('UPDATE users SET failed_login = 0, last_failed_at = NULL WHERE id = :id');
  109.         return $reset->execute([':id' => $userId]);
  110.     }
  111.  
  112.     /**
  113.      * Set flash error, preserve attempted username, and redirect back to login.
  114.      *
  115.      * @param string $message The error message to display.
  116.      */
  117.     public function bounce(string $message): void
  118.     {
  119.         // Preserve the last entered username for UX
  120.         $old = trim((string) filter_input(INPUT_POST, 'username', FILTER_SANITIZE_FULL_SPECIAL_CHARS));
  121.         $_SESSION['old_username'] = $old;
  122.         $_SESSION['login_error'] = $message;
  123.         header('Location: ' . BASE_PATH . 'login');
  124.         exit;
  125.     }
  126. }
Advertisement
Add Comment
Please, Sign In to add comment