web-token/jwt-easy
Simple JWT helper for PHP apps: quickly encode, decode, and validate JSON Web Tokens with minimal setup. Designed for straightforward auth flows, with easy-to-use APIs for signing, verifying, and reading claims.
Installation
composer require web-token/jwt-easy
Add to config/app.php under providers:
WebToken\JWTEasy\JWTEasyServiceProvider::class,
Publish Config
php artisan vendor:publish --provider="WebToken\JWTEasy\JWTEasyServiceProvider"
Configure config/jwt-easy.php with your secret key, algorithm, and issuer.
First Use Case: Generate a Token
use WebToken\JWTEasy\Facades\JWTEasy;
$token = JWTEasy::encode(['user_id' => 123, 'role' => 'admin']);
Verify a Token
$payload = JWTEasy::decode($token);
config/jwt-easy.php (Configuration)app/Providers/AppServiceProvider.php (Service binding, if customized)app/Http/Middleware/ (Middleware for auth, if implemented)$payload = [
'user_id' => auth()->id(),
'permissions' => ['create', 'read'],
'exp' => now()->addHours(1)->timestamp, // Custom expiration
];
$token = JWTEasy::encode($payload);
Create a middleware to verify tokens on protected routes:
// app/Http/Middleware/VerifyJWT.php
public function handle($request, Closure $next)
{
$token = $request->bearerToken();
if (!$token) {
return response()->json(['error' => 'Unauthorized'], 401);
}
try {
$payload = JWTEasy::decode($token);
$request->merge(['user' => $payload]);
return $next($request);
} catch (\Exception $e) {
return response()->json(['error' => 'Invalid token'], 401);
}
}
Register in app/Http/Kernel.php:
protected $routeMiddleware = [
'jwt.verify' => \App\Http\Middleware\VerifyJWT::class,
];
Use in routes:
Route::middleware('jwt.verify')->group(function () {
// Protected routes
});
Store a refresh_token in the database (e.g., refresh_tokens table) and issue a new access token when expired:
$refreshToken = $request->input('refresh_token');
$storedToken = RefreshToken::where('token', $refreshToken)->first();
if ($storedToken && !$storedToken->revoked) {
$newToken = JWTEasy::encode([
'user_id' => $storedToken->user_id,
'exp' => now()->addHours(1)->timestamp,
]);
return response()->json(['access_token' => $newToken]);
}
Use JWT for API auth while keeping Sanctum/Passport for session-based auth:
// In a controller
if ($request->hasHeader('Authorization')) {
$token = $request->bearerToken();
$payload = JWTEasy::decode($token);
$user = User::find($payload['user_id']);
auth()->login($user);
}
tokens table with user_id, token, expires_at, and revoked fields.HasApiTokens trait for Eloquent models if needed.JWTEasy facade in tests:
$this->partialMock(JWTEasy::class, function ($mock) {
$mock->shouldReceive('decode')
->once()
->andReturn(['user_id' => 1]);
});
\Log::info('JWT generated', ['payload' => $payload, 'token' => $token]);
Secret Key Management
.env:
JWT_SECRET=your_secure_random_key_here
Time Skew Issues
exp claims are misaligned.now()->getTimestamp() instead of time() for consistency.Algorithm Mismatch
HS256, ensure the secret key is at least 32 bytes. For RS256, manage private/public keys securely.Revocation Without Database
Payload Size Limits
jti (JWT ID) and fetch data from the database.Invalid Token Errors
exp claim isn’t in the past.HS256, RS256, etc.) is consistent.Facade Not Found
Slow Decoding
jti for lookups instead.Custom Claims
Add a JWTClaims class to standardize claims:
class JWTClaims {
public static function user($user) {
return [
'sub' => $user->id,
'name' => $user->name,
'iat' => now()->timestamp,
'exp' => now()->addHour()->timestamp,
];
}
}
Event Listeners Trigger events on token generation/decoding:
JWTEasy::extend(function ($jwt) {
$jwt->onEncode(function ($payload) {
event(new TokenGenerated($payload));
});
});
Rate Limiting Limit token generation attempts to prevent brute force:
use Illuminate\Cache\RateLimiter;
$limiter = RateLimiter::for('jwt-generation')->by($request->ip());
if (!$limiter->tooManyAttempts(5, 1)) {
$token = JWTEasy::encode($payload);
}
Multi-Tenancy
Add a tenant_id claim and middleware to scope requests:
$payload['tenant_id'] = auth()->user()->tenant_id;
// Middleware
Tenant::set($request->user()['tenant_id']);
exp claim.HS256. Change in config/jwt-easy.php:
'algorithm' => 'RS256',
How can I help you explore Laravel packages today?