<?php
namespace App\Services\Service;
use App\Services\Implement\JwtInterface;
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Token\Plain;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
use Lcobucci\JWT\Validation\Constraint\ValidAt;
use Lcobucci\Clock\SystemClock;
use Redis;
class JwtService implements JwtInterface {
public Configuration $jwtConfig;
private int $jwtExp;
private Redis $redis;
public function __construct() {
$config = \PhalApi\DI()->config->get('app.jwt');
$this->jwtExp = $config['exp'];
$this->jwtConfig = Configuration::forSymmetricSigner(
new Sha256(),
InMemory::plainText($config['secret'])
);
$this->redis = \PhalApi\DI()->redis->getRedis();
}
public function generateToken($user): string {
$now = new \DateTimeImmutable();
$exp = $now->modify('+' . $this->jwtExp . ' seconds');
$token = $this->jwtConfig->builder()
->issuedBy('your_domain.com')
->permittedFor('your_domain.com')
->issuedAt($now)
->expiresAt($exp)
->withClaim('id', $user['id'])
->withClaim('username', $user['username'])
->withClaim('role', $user['role_id'])
->getToken($this->jwtConfig->signer(), $this->jwtConfig->signingKey());
return $token->toString();
}
public function validateToken($token): bool {
if ($this->isTokenBlacklisted($token)) {
return false;
}
try {
$token = $this->jwtConfig->parser()->parse($token);
assert($token instanceof Plain);
$constraints = [
new SignedWith($this->jwtConfig->signer(), $this->jwtConfig->verificationKey()),
new ValidAt(SystemClock::fromUTC())
];
$this->jwtConfig->validator()->assert($token, ...$constraints);
return true;
} catch (\Exception $e) {
return false;
}
}
public function getUserIdFromToken($token): ?int {
try {
$token = $this->jwtConfig->parser()->parse($token);
assert($token instanceof Plain);
return $token->claims()->get('id');
} catch (\Exception $e) {
return null;
}
}
public function addTokenToBlacklist(string $token, int $exp) {
$this->redis->setex($token, $exp, 'blacklisted');
}
public function isTokenBlacklisted(string $token): bool {
return $this->redis->exists($token) === 1;
}
}