widimulcing

AuthService.php

Jul 29th, 2025 (edited)
244
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 2.55 KB | None | 0 0
  1. <?php
  2. // 4o-mini-high
  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. declare(strict_types=1);
  14.  
  15. namespace App\Auth;
  16.  
  17. use PDO;
  18. use RuntimeException;
  19.  
  20. class AuthService
  21. {
  22.     private PDO $pdo;
  23.     private int $maxAttempts = 5;
  24.     private int $lockDuration = 86400; // 24 hours in seconds
  25.  
  26.     public function __construct(PDO $pdo)
  27.     {
  28.         $this->pdo = $pdo;
  29.     }
  30.  
  31.     /**
  32.      * @param string $username
  33.      * @param string $password
  34.      * @return int   The authenticated user's ID
  35.      * @throws RuntimeException on invalid credentials or lockout
  36.      */
  37.     public function authenticate(string $username, string $password): int
  38.     {
  39.         $stmt = $this->pdo->prepare(
  40.             'SELECT id, password_hash, failed_login, last_failed_at
  41.               FROM users
  42.              WHERE username = :username
  43.              LIMIT 1'
  44.         );
  45.         $stmt->execute([':username' => $username]);
  46.         $user = $stmt->fetch(PDO::FETCH_ASSOC);
  47.  
  48.         if (!$user) {
  49.             throw new RuntimeException('Invalid username or password.');
  50.         }
  51.  
  52.         $failedLogin  = (int)$user['failed_login'];
  53.         $lastFailedAt = $user['last_failed_at']
  54.             ? strtotime($user['last_failed_at'])
  55.             : 0;
  56.         $now = time();
  57.  
  58.         // lockout check
  59.         if ($failedLogin >= $this->maxAttempts
  60.             && ($now - $lastFailedAt) < $this->lockDuration
  61.         ) {
  62.             throw new RuntimeException('Account locked. Try again later.');
  63.         }
  64.  
  65.         // password verify
  66.         if (!password_verify($password, (string)$user['password_hash'])) {
  67.             $update = $this->pdo->prepare(
  68.                 'UPDATE users
  69.                    SET failed_login   = failed_login + 1,
  70.                        last_failed_at = NOW()
  71.                  WHERE id = :id'
  72.             );
  73.             $update->execute([':id' => $user['id']]);
  74.             throw new RuntimeException('Invalid username or password.');
  75.         }
  76.  
  77.         // reset failure counter
  78.         $reset = $this->pdo->prepare(
  79.             'UPDATE users
  80.                SET failed_login   = 0,
  81.                    last_failed_at = NULL
  82.              WHERE id = :id'
  83.         );
  84.         $reset->execute([':id' => $user['id']]);
  85.  
  86.         return (int)$user['id'];
  87.     }
  88. }
  89.  
Advertisement
Add Comment
Please, Sign In to add comment