50 lines
1.3 KiB
PHP
50 lines
1.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services\Security;
|
|
|
|
use Firebase\JWT\JWT;
|
|
use Firebase\JWT\Key;
|
|
use Exception;
|
|
|
|
final class JwtService
|
|
{
|
|
private string $secret;
|
|
private int $accessExpiry;
|
|
private int $refreshExpiry;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->secret = $_ENV['JWT_SECRET'] ?? 'change-me';
|
|
$this->accessExpiry = (int)($_ENV['JWT_ACCESS_EXPIRY'] ?? 900);
|
|
$this->refreshExpiry = (int)($_ENV['JWT_REFRESH_EXPIRY'] ?? 604800);
|
|
}
|
|
|
|
public function issueAccessToken(array $payload): string
|
|
{
|
|
$payload['exp'] = time() + $this->accessExpiry;
|
|
$payload['iat'] = time();
|
|
$payload['jti'] = bin2hex(random_bytes(16));
|
|
|
|
return JWT::encode($payload, $this->secret, 'HS256');
|
|
}
|
|
|
|
public function issueRefreshToken(string $userId): string
|
|
{
|
|
// Refresh token is a random string prefixed with userId for lookup
|
|
$random = bin2hex(random_bytes(32));
|
|
return $userId . '.' . $random;
|
|
}
|
|
|
|
public function verifyToken(string $token): array
|
|
{
|
|
try {
|
|
$decoded = JWT::decode($token, new Key($this->secret, 'HS256'));
|
|
return (array) $decoded;
|
|
} catch (Exception $e) {
|
|
throw new Exception("Invalid or expired token: " . $e->getMessage());
|
|
}
|
|
}
|
|
}
|