Files
scoutiq/app/Services/Auth/AuthService.php

147 lines
4.4 KiB
PHP

<?php
namespace App\Services\Auth;
use App\Services\Database\Connection;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use PDO;
use Exception;
use Throwable;
class AuthService
{
private PDO $pdo;
private array $jwtConfig;
public function __construct(Connection $connection)
{
$this->pdo = $connection->getPdo();
$aiConfig = require __DIR__ . '/../../../config/ai.php';
$this->jwtConfig = $aiConfig['jwt'];
}
/**
* Register a new user.
*/
public function register(string $name, string $email, string $password): array
{
// Check for duplicates
$stmt = $this->pdo->prepare("SELECT id FROM users WHERE email = ?");
$stmt->execute([$email]);
if ($stmt->fetch()) {
throw new Exception("Email already registered.");
}
$passwordHash = password_hash($password, PASSWORD_BCRYPT);
$this->pdo->beginTransaction();
try {
$stmt = $this->pdo->prepare("INSERT INTO users (name, email, password_hash, status) VALUES (?, ?, ?, 'active')");
$stmt->execute([$name, $email, $passwordHash]);
$userId = (int)$this->pdo->lastInsertId();
// Count users to assign role: first user gets Admin, others get Member
$stmt = $this->pdo->query("SELECT COUNT(*) FROM users");
$count = (int)$stmt->fetchColumn();
$roleCode = $count === 1 ? 'admin' : 'member';
$stmt = $this->pdo->prepare("SELECT id FROM roles WHERE code = ?");
$stmt->execute([$roleCode]);
$roleId = $stmt->fetchColumn();
if ($roleId) {
$stmt = $this->pdo->prepare("INSERT INTO user_roles (user_id, role_id) VALUES (?, ?)");
$stmt->execute([$userId, $roleId]);
}
$this->pdo->commit();
return [
'id' => $userId,
'name' => $name,
'email' => $email,
'status' => 'active'
];
} catch (Throwable $e) {
$this->pdo->rollBack();
throw new Exception("Registration failed: " . $e->getMessage());
}
}
/**
* Authenticate a user by email and password.
*/
public function login(string $email, string $password): array
{
$stmt = $this->pdo->prepare("SELECT id, name, email, password_hash, status FROM users WHERE email = ? AND deleted_at IS NULL");
$stmt->execute([$email]);
$user = $stmt->fetch();
if (!$user || !password_verify($password, $user['password_hash'])) {
throw new Exception("Invalid email or password.");
}
if ($user['status'] !== 'active') {
throw new Exception("User account is inactive.");
}
unset($user['password_hash']);
return $user;
}
/**
* Get active user by ID.
*/
public function getUserById(int $id): ?array
{
$stmt = $this->pdo->prepare("SELECT id, name, email, status FROM users WHERE id = ? AND deleted_at IS NULL");
$stmt->execute([$id]);
$user = $stmt->fetch();
return $user ?: null;
}
/**
* Generate JWT for APIs.
*/
public function generateJwt(array $user): string
{
$issuedAt = time();
$expire = $issuedAt + $this->jwtConfig['expires_in'];
$payload = [
'iss' => $_ENV['APP_URL'] ?? 'https://scoutiq.intaleqapp.com',
'aud' => $_ENV['APP_URL'] ?? 'https://scoutiq.intaleqapp.com',
'iat' => $issuedAt,
'exp' => $expire,
'sub' => $user['id'],
'user' => [
'id' => $user['id'],
'name' => $user['name'],
'email' => $user['email']
]
];
return JWT::encode($payload, $this->jwtConfig['secret'], $this->jwtConfig['algorithm']);
}
/**
* Decode and verify JWT.
*/
public function verifyJwt(string $token): ?array
{
try {
$decoded = JWT::decode($token, new Key($this->jwtConfig['secret'], $this->jwtConfig['algorithm']));
$payload = (array)$decoded;
if (isset($payload['user'])) {
return (array)$payload['user'];
}
return $this->getUserById((int)$payload['sub']);
} catch (Throwable $e) {
return null;
}
}
}