Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- // Gemini
- /**
- * @file AuthService.php
- * @description Contains the core business logic for user authentication.
- * It handles database interactions, password verification, and
- * brute-force protection logic, independent of HTTP context.
- *
- * @version 1.0.0
- * @since 2025-07-25
- * @author Barrac0de
- */
- declare(strict_types=1);
- namespace App\Auth;
- use PDO;
- class AuthService
- {
- private PDO $pdo;
- private const MAX_ATTEMPTS = 5;
- private const LOCK_TIME = 24 * 60 * 60; // 24 hours in seconds
- public function __construct(PDO $pdo)
- {
- $this->pdo = $pdo;
- }
- /**
- * Attempts to log a user in with the given credentials.
- *
- * @param string $username The user's username.
- * @param string $password The user's password.
- * @return array An array with 'success' (bool) and 'message' or 'user_id'.
- */
- public function attemptLogin(string $username, string $password): array
- {
- $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) {
- // Use a generic message to prevent username enumeration
- return ['success' => false, 'message' => 'Invalid username or password.'];
- }
- // Brute-force protection check
- $failedLogin = (int)$user['failed_login'];
- $lastFailedAt = $user['last_failed_at'] ? strtotime($user['last_failed_at']) : 0;
- if ($failedLogin >= self::MAX_ATTEMPTS && (time() - $lastFailedAt) < self::LOCK_TIME) {
- return ['success' => false, 'message' => 'Account locked. Try again later.'];
- }
- // Verify password
- if (!password_verify($password, (string)$user['password_hash'])) {
- $this->incrementFailedAttempts($user['id']);
- return ['success' => false, 'message' => 'Invalid username or password.'];
- }
- // Login is successful
- $this->resetFailedAttempts($user['id']);
- return ['success' => true, 'user_id' => (int)$user['id']];
- }
- /**
- * Increments the failed login counter for a user.
- * @param int $userId The ID of the user.
- */
- private function incrementFailedAttempts(int $userId): void
- {
- $stmt = $this->pdo->prepare(
- 'UPDATE users SET failed_login = failed_login + 1, last_failed_at = NOW() WHERE id = :id'
- );
- $stmt->execute([':id' => $userId]);
- }
- /**
- * Resets the failed login counter for a user.
- * @param int $userId The ID of the user.
- */
- private function resetFailedAttempts(int $userId): void
- {
- $stmt = $this->pdo->prepare(
- 'UPDATE users SET failed_login = 0, last_failed_at = NULL WHERE id = :id'
- );
- $stmt->execute([':id' => $userId]);
- }
- /**
- * Checks if the user session has timed out.
- */
- public static function checkSessionTimeout(): void
- {
- $timeout = 3600; // 1 hour
- if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity']) > $timeout) {
- session_unset();
- session_destroy();
- header('Location: ' . BASE_PATH . 'login?timeout=1');
- exit;
- }
- // Update last activity time on each request
- if(isset($_SESSION['user_id'])) {
- $_SESSION['last_activity'] = time();
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment