provider = $provider; $this->session = $session; $this->config = array_merge([ 'cookie_name' => self::REMEMBER_COOKIE, 'cookie_lifetime' => self::REMEMBER_DURATION, 'cookie_path' => '/', 'cookie_domain' => '', 'cookie_secure' => false, 'cookie_httponly' => true, 'cookie_samesite' => 'Lax' ], $config); $this->initializeFromSession(); } /** * Attempt to authenticate user with credentials */ public function attempt(array $credentials, bool $remember = false): bool { $identifier = $credentials['email'] ?? $credentials['username'] ?? null; $password = $credentials['password'] ?? null; if (!$identifier || !$password) { return false; } $user = $this->provider->findByCredentials($identifier); if (!$user || !$this->provider->verifyPassword($user, $password)) { return false; } $this->login($user, $remember); return true; } /** * Login a user instance */ public function login(User $user, bool $remember = false): void { $this->user = $user; $this->session->set(self::SESSION_KEY, $user->getId()); $this->session->regenerate(); if ($remember) { $this->createRememberToken($user); } } /** * Login using user ID */ public function loginById(int|string $id, bool $remember = false): bool { $user = $this->provider->findById($id); if (!$user) { return false; } $this->login($user, $remember); return true; } /** * Logout the current user */ public function logout(): void { if ($this->user && $this->user->rememberToken) { $this->provider->updateRememberToken($this->user, null); } $this->user = null; $this->session->remove(self::SESSION_KEY); $this->session->regenerate(); $this->clearRememberCookie(); } /** * Check if user is authenticated */ public function check(): bool { return $this->user() !== null; } /** * Check if user is guest (not authenticated) */ public function guest(): bool { return !$this->check(); } /** * Get the currently authenticated user */ public function user(): ?User { if ($this->user) { return $this->user; } // Try to load from session $userId = $this->session->get(self::SESSION_KEY); if ($userId) { $this->user = $this->provider->findById($userId); return $this->user; } // Try to load from remember cookie $this->user = $this->getUserFromRememberCookie(); if ($this->user) { $this->session->set(self::SESSION_KEY, $this->user->getId()); } return $this->user; } /** * Get user ID */ public function id(): int|string|null { return $this->user()?->getId(); } /** * Register a new user */ public function register(array $data, bool $login = true): User { // Hash password before storing if (isset($data['password'])) { $data['password'] = $this->hashPassword($data['password']); } $user = $this->provider->create($data); if ($login) { $this->login($user); } return $user; } /** * Validate user credentials without logging in */ public function validate(array $credentials): bool { $identifier = $credentials['email'] ?? $credentials['username'] ?? null; $password = $credentials['password'] ?? null; if (!$identifier || !$password) { return false; } $user = $this->provider->findByCredentials($identifier); return $user && $this->provider->verifyPassword($user, $password); } /** * Hash a password */ public function hashPassword(string $password): string { return password_hash($password, PASSWORD_BCRYPT, ['cost' => 10]); } /** * Verify a password against hash */ public function verifyPassword(string $password, string $hash): bool { return password_verify($password, $hash); } /** * Initialize user from session */ private function initializeFromSession(): void { if (!$this->session->isStarted()) { $this->session->start(); } $this->user(); } /** * Create remember token for user */ private function createRememberToken(User $user): void { $token = $this->generateRememberToken(); $hashedToken = hash('sha256', $token); $this->provider->updateRememberToken($user, $hashedToken); $user->rememberToken = $hashedToken; $this->setRememberCookie($user->getId() . '|' . $token); } /** * Generate a random remember token */ private function generateRememberToken(): string { return bin2hex(random_bytes(32)); } /** * Set remember cookie */ private function setRememberCookie(string $value): void { setcookie( $this->config['cookie_name'], $value, [ 'expires' => time() + $this->config['cookie_lifetime'], 'path' => $this->config['cookie_path'], 'domain' => $this->config['cookie_domain'], 'secure' => $this->config['cookie_secure'], 'httponly' => $this->config['cookie_httponly'], 'samesite' => $this->config['cookie_samesite'] ] ); } /** * Clear remember cookie */ private function clearRememberCookie(): void { setcookie( $this->config['cookie_name'], '', [ 'expires' => time() - 3600, 'path' => $this->config['cookie_path'], 'domain' => $this->config['cookie_domain'], 'secure' => $this->config['cookie_secure'], 'httponly' => $this->config['cookie_httponly'], 'samesite' => $this->config['cookie_samesite'] ] ); } /** * Get user from remember cookie */ private function getUserFromRememberCookie(): ?User { $cookie = $_COOKIE[$this->config['cookie_name']] ?? null; if (!$cookie || !str_contains($cookie, '|')) { return null; } [$id, $token] = explode('|', $cookie, 2); $hashedToken = hash('sha256', $token); $user = $this->provider->findById($id); if (!$user || $user->rememberToken !== $hashedToken) { $this->clearRememberCookie(); return null; } return $user; } /** * Refresh user instance from provider */ public function refresh(): void { if ($this->user) { $this->user = $this->provider->findById($this->user->getId()); } } /** * Set the current user */ public function setUser(User $user): void { $this->user = $user; $this->session->set(self::SESSION_KEY, $user->getId()); } }