kylesean / hyperf-jwt
A JWT (JSON Web Token) package for Hyperf framework.
Requires
- php: ^8.2
- hyperf/cache: ^3.0 || ^3.1
- hyperf/contract: ^3.0 || ^3.1
- hyperf/stringable: ^3.0 || ^3.1
- lcobucci/jwt: ^5.4
- psr/clock: ^1.0
- psr/simple-cache: ^2.0 || ^3.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- hyperf/command: ^3.1
- hyperf/framework: ^3.0 || ^3.1
- hyperf/http-server: ^3.1
- hyperf/support: ^3.1
- mockery/mockery: ^1.6
- phpstan/phpstan: ^1.0
- phpunit/phpunit: ^10.0 || ^11.0
- psr/container: ^1.0 || ^2.0
- swoole/ide-helper: dev-master
- symfony/var-dumper: ^6.0 || ^7.0
README
English | 中文文档
A high-performance, lightweight JWT (JSON Web Token) package designed for Hyperf coroutine framework, powered by lcobucci/jwt v5.
Features
- Coroutine Friendly: Native integration with Hyperf DI container and Swoole/Swow coroutine concurrency environments.
- Multiple Algorithms: Full support for HMAC (HS256, HS384, HS512), RSA (RS256, etc.), and ECDSA (ES256, etc.) signing algorithms.
- Blacklist & Concurrency Grace Period: Redis/Cache-backed token blacklisting with an innovative Concurrency Grace Period mechanism for coroutine applications.
- Flexible Request Parsing: Extract tokens from Authorization Header (Bearer), URL Query Parameters, POST Body, or Cookies in customizable order.
- Seamless Authentication Middleware: Out-of-the-box
JwtAuthMiddlewarewith coroutine context isolation and convenient static helpers.
Installation
Install via Composer:
composer require kylesean/hyperf-jwt
Publish the configuration file:
php bin/hyperf.php vendor:publish kylesean/hyperf-jwt
Generate a secure secret key:
php bin/hyperf.php jwt:gen-key --algo=hs256 --update-env
Quick Start
1. Token Issuance & Parsing
use Kylesean\Jwt\Contract\ManagerInterface; use Hyperf\Context\ApplicationContext; $manager = ApplicationContext::getContainer()->get(ManagerInterface::class); // 1. Issue a Token with custom claims and subject $token = $manager->issueToken([ 'user_id' => 123, 'role' => 'admin' ], 'user_123'); $tokenString = $token->toString(); // 2. Parse and validate a Token string $parsedToken = $manager->parse($tokenString); $userId = $parsedToken->getClaim('user_id'); // 123 $subject = $parsedToken->getSubject(); // 'user_123'
2. Token Refreshing
The ManagerInterface::refreshToken() method allows clients to swap an expiring token for a fresh token within the configured refresh window (refresh_ttl), automatically blacklisting the old token.
use Kylesean\Jwt\Exception\TokenExpiredException; use Kylesean\Jwt\Exception\TokenInvalidException; try { // Refresh the old token and get a new Token instance // Param 1: Old token string // Param 2: forceForever (Whether to permanently blacklist the old token) // Param 3: resetClaims (Whether to reset custom claims on the new token, default false) $newToken = $manager->refreshToken($oldTokenString); echo $newToken->toString(); } catch (TokenExpiredException $e) { // Old token has exceeded the refresh TTL window } catch (TokenInvalidException $e) { // Old token is invalid, tampered with, or already blacklisted }
3. Token Invalidation & Blacklist Grace Period
Manual Invalidation (Logout)
// Add the given token to the blacklist immediately $manager->invalidate($token);
The blacklist entry for an invalidated token is kept until the token's natural expiry plus the full refresh_ttl window, so a logged-out token can never be "revived" by refreshing it later. Pass true as the second argument to keep the entry for one year ("forever") instead: $manager->invalidate($token, true).
Coroutine Concurrency Grace Period
In high-concurrency coroutine environments (e.g. 5 parallel HTTP requests sent by a Single Page App simultaneously), if one request refreshes the token and invalidates the old one immediately, the remaining 4 concurrent requests carrying the old token might trigger 401 Unauthorized errors.
Configure the concurrency grace period in config/autoload/jwt.php:
'blacklist_concurrency_grace_period' => 30, // 30 seconds grace period
During this 30-second window, the replaced old token remains accepted as valid, preventing race-condition failures.
Concurrency semantics: the grace period guarantees that concurrent requests validating the old token do not fail. Blacklisting itself is a non-atomic check-then-set against the cache, so two refresh calls arriving at the exact same instant may both succeed and each receive a new token (the old token still ends up blacklisted). If your business logic requires strictly single-use refresh, add a distributed lock around
refreshToken().
4. Authentication Middleware
Register JwtAuthMiddleware in your routes or controller annotations:
use Kylesean\Jwt\Middleware\JwtAuthMiddleware; use Hyperf\HttpServer\Router\Router; Router::addGroup('/api', function () { Router::get('/user/profile', [UserController::class, 'profile']); }, ['middleware' => [JwtAuthMiddleware::class]]);
Access authenticated identity inside controllers:
use Kylesean\Jwt\Middleware\JwtAuthMiddleware; class UserController { public function profile() { // Retrieve current authenticated Token / Subject from Coroutine Context $subject = JwtAuthMiddleware::getSubject(); $role = JwtAuthMiddleware::getClaim('role'); return [ 'user' => $subject, 'role' => $role ]; } }