Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- // Claude
- /**
- * @file AuthService.php
- * @description Service class for authentication operations including user verification,
- * password validation, and brute-force protection.
- *
- * @version 1.0.0
- * @since 2025-07-29
- * @author Barrac0de
- */
- declare(strict_types=1);
- namespace App\Auth;
- use PDO;
- class AuthService
- {
- /** @var PDO */
- private $pdo;
- /** @var int */
- private $maxAttempts = 5;
- /** @var int */
- private $lockDuration = 86400; // 24 hours in seconds
- /**
- * Constructor
- *
- * @param PDO $pdo Database connection
- */
- public function __construct(PDO $pdo)
- {
- $this->pdo = $pdo;
- }
- /**
- * Verify user credentials and handle login attempts
- *
- * @param string $username The username to verify
- * @param string $password The password to verify
- * @return array|false User data if authentication successful, false otherwise
- */
- public function authenticate(string $username, string $password)
- {
- if ($username === '' || $password === '') {
- $this->bounce('Username and password are required.');
- return false;
- }
- // Fetch user record
- $stmt = $this->pdo->prepare('SELECT id, password_hash, failed_login, last_failed_at FROM users WHERE username = :username LIMIT 1');
- $stmt->execute([':username' => $username]);
- $user = $stmt->fetch(PDO::FETCH_ASSOC);
- if (!$user) {
- $this->bounce('Invalid username or password.');
- return false;
- }
- // Check for account lockout
- $failedLogin = (int)$user['failed_login'];
- $lastFailedAt = $user['last_failed_at'] ? strtotime($user['last_failed_at']) : 0;
- $now = time();
- if ($failedLogin >= $this->maxAttempts && ($now - $lastFailedAt) < $this->lockDuration) {
- $this->bounce('Account locked. Try again later.');
- return false;
- }
- // Verify password
- if (!password_verify($password, (string)$user['password_hash'])) {
- $this->incrementFailedLogin($user['id']);
- $this->bounce('Invalid username or password.');
- return false;
- }
- // Reset failed login counter on successful login
- $this->resetFailedLogin($user['id']);
- return $user;
- }
- /**
- * Increment the failed login counter for a user
- *
- * @param int $userId The user ID
- * @return bool Success status
- */
- private function incrementFailedLogin(int $userId): bool
- {
- $update = $this->pdo->prepare(
- 'UPDATE users
- SET failed_login = failed_login + 1, last_failed_at = NOW()
- WHERE id = :id'
- );
- return $update->execute([':id' => $userId]);
- }
- /**
- * Reset the failed login counter for a user
- *
- * @param int $userId The user ID
- * @return bool Success status
- */
- private function resetFailedLogin(int $userId): bool
- {
- $reset = $this->pdo->prepare('UPDATE users SET failed_login = 0, last_failed_at = NULL WHERE id = :id');
- return $reset->execute([':id' => $userId]);
- }
- /**
- * Set flash error, preserve attempted username, and redirect back to login.
- *
- * @param string $message The error message to display.
- */
- public function bounce(string $message): void
- {
- // Preserve the last entered username for UX
- $old = trim((string) filter_input(INPUT_POST, 'username', FILTER_SANITIZE_FULL_SPECIAL_CHARS));
- $_SESSION['old_username'] = $old;
- $_SESSION['login_error'] = $message;
- header('Location: ' . BASE_PATH . 'login');
- exit;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment