mishal/jwt
Lightweight PHP JSON Web Token (JWT) library with support for None, HS256 (HMAC SHA-256), and RS256 (RSA SHA-256). Simple encode/decode API with allowed-algorithm validation and support for standard reserved claims like exp, nbf, iss, and aud.
composer require mishal/jwt
use Jwt\Jwt;
use Jwt\Algorithm\HS256Algorithm;
$secret = config('jwt.secret'); // Store in config/jwt.php
$algorithm = new HS256Algorithm($secret);
$payload = ['user_id' => 123, 'role' => 'admin'];
$token = Jwt::encode($payload, $algorithm);
try {
$decoded = Jwt::decode($token, ['algorithm' => $algorithm]);
$userId = $decoded['user_id'];
} catch (\Jwt\Exception\ExpiredException $e) {
abort(401, 'Token expired');
}
Authorization: Bearer <token> headers.// app/Http/Middleware/AuthenticateJWT.php
public function handle($request, Closure $next) {
$token = $request->bearerToken();
if (!$token) return response()->json(['error' => 'Unauthorized'], 401);
try {
$decoded = Jwt::decode($token, ['algorithm' => $this->algorithm]);
$request->merge(['user' => $decoded]);
return $next($request);
} catch (\Exception $e) {
return response()->json(['error' => 'Invalid token'], 401);
}
}
$payload = [
'user_id' => auth()->id(),
Jwt::CLAIM_EXPIRATION => now()->addHours(1)->getTimestamp(),
Jwt::CLAIM_ISSUER => config('app.name'),
'metadata' => ['ip' => request()->ip()]
];
// app/Http/Kernel.php
protected $middleware = [
\App\Http\Middleware\AuthenticateJWT::class,
];
Route::middleware(['jwt'])->group(function () {
Route::get('/admin', 'AdminController@index');
});
$verify = [
Jwt::CLAIM_ISSUER => config('app.name'),
'role' => function ($role) {
return in_array($role, ['admin', 'editor']);
},
Jwt::CLAIM_AUDIENCE => ['api', 'mobile-app']
];
Jwt::decode($token, ['algorithm' => $algorithm, 'verify' => $verify]);
// Issue refresh token (RS256, expires in 30 days)
$refreshToken = Jwt::encode([
'user_id' => 123,
Jwt::CLAIM_EXPIRATION => now()->addDays(30)->getTimestamp(),
'token_type' => 'refresh'
], $rsaAlgorithm);
// Issue access token (HS256, expires in 1 hour)
$accessToken = Jwt::encode([
'user_id' => 123,
Jwt::CLAIM_EXPIRATION => now()->addHour()->getTimestamp(),
'refresh_token' => $refreshToken
], $hsAlgorithm);
$payload = [
'user_id' => 123,
'permissions' => ['create_post', 'edit_profile'],
'tier' => 'premium'
];
Algorithm Security:
NoneAlgorithm in production (tokens are unsigned).Jwt::decode($token, ['algorithm' => [$hsAlgorithm, $rsaAlgorithm]]);
Clock Skew:
leeway for exp/nbf claims to account for server time differences:
Jwt::decode($token, ['algorithm' => $algorithm, 'leeway' => 60]); // 60 sec
Secret Management:
.env or vault).openssl to generate:
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in private_key.pem -out public_key.pem
Token Size Limits:
Error Handling:
try {
$decoded = Jwt::decode($token, ['algorithm' => $algorithm]);
} catch (\Jwt\Exception\SignatureInvalidException $e) {
return response()->json(['error' => 'Invalid token signature'], 401);
} catch (\Jwt\Exception\TokenNotProvidedException $e) {
return response()->json(['error' => 'Token not provided'], 401);
}
$decoded = Jwt::decode($token, ['algorithm' => $algorithm, 'verify' => false]);
list($header, $payload, $signature) = explode('.', $token);
$decodedPayload = base64_decode(strtr($payload, '-_', '+/'));
try {
$decoded = Jwt::decode($token, ['algorithm' => $algorithm, 'verify' => $verify]);
} catch (\Jwt\Exception\VerificationException $e) {
\Log::error("JWT Verification Failed", [
'claim' => $e->getClaim(),
'code' => $e->getCode(),
'token' => $token
]);
throw $e;
}
Custom Algorithms:
Jwt\Algorithm\AlgorithmInterface for unsupported algorithms (e.g., ECDSA).Payload Transformers:
$payload = [
'user' => User::find($userId)->toArray(),
'metadata' => ['device' => request()->userAgent()]
];
Token Storage:
same-site and secure flags for cookies:
$response = response()->json(['token' => $token]);
$response->withCookie(cookie('jwt', $token, 60 * 24, null, null, true, true));
Revocation:
// Before decoding, check revocation status
if (Token::where('token', $token)->where('revoked_at', null)->exists()) {
throw new \Jwt\Exception\TokenRevokedException();
}
aud (audience) claims to restrict token usage to specific clients.How can I help you explore Laravel packages today?