kelvinmo/simplejwt
SimpleJWT is a PHP 8+ library for creating, signing, verifying, and encrypting JSON Web Tokens (JWT/JWS/JWE). Supports JWK/COSE keys, HMAC/RSA/ECDSA/EdDSA, key management (RSA-OAEP, ECDH-ES, PBES2), and AES-GCM/CBC-HS encryption.
Installation:
composer require kelvinmo/simplejwt
Ensure your composer.json includes PHP 8.0+ and extensions: gmp, hash, openssl, and sodium (for EdDSA/X25519).
First Use Case: Generate a JWT for API authentication:
use SimpleJWT\Keys\KeySet;
use SimpleJWT\JWT;
$keySet = KeySet::createFromSecret('your-secret-key');
$headers = ['alg' => 'HS256', 'typ' => 'JWT'];
$claims = ['sub' => 'user123', 'exp' => time() + 3600];
$jwt = new JWT($headers, $claims);
$token = $jwt->encode($keySet);
Where to Look First:
SimpleJWT\Keys\KeySet for key management.SimpleJWT\JWT and SimpleJWT\JWE for token operations.HMAC Secrets:
$keySet = KeySet::createFromSecret('secret-key');
Use for stateless APIs (e.g., API gateways).
Asymmetric Keys (RSA/ECDSA):
$privateKey = new \SimpleJWT\Keys\RSAKey(file_get_contents('private.pem'), 'pem');
$publicKey = new \SimpleJWT\Keys\RSAKey(file_get_contents('public.pem'), 'pem');
$keySet->add($privateKey, true); // Auto-generate kid
$keySet->add($publicKey);
Ideal for server-to-server or user authentication with public/private pairs.
Key Rotation:
$keySet->add($newPrivateKey, true); // Add new key with kid
$keySet->remove($oldPrivateKey); // Remove old key
Store keys in environment variables or a secrets manager (e.g., AWS Secrets Manager).
JWT Creation:
$jwt = new JWT(['alg' => 'RS256'], ['sub' => 'user123', 'iat' => time()]);
$token = $jwt->encode($keySet);
HS256 for simplicity, RS256/ES256 for security.kid/iat with $jwt->encode($keySet, false).JWT Validation:
try {
$decoded = JWT::decode($token, $keySet, 'RS256');
$claims = $decoded->getClaims();
} catch (\SimpleJWT\InvalidTokenException $e) {
// Handle invalid token (expired, tampered, etc.)
}
alg against a whitelist (e.g., ['HS256', 'RS256']).JWT::deserialise() for debugging (no validation).JWE (Encrypted Tokens):
$jwe = new \SimpleJWT\JWE(['alg' => 'PBES2-HS256+A128KW', 'enc' => 'A128CBC-HS256'], 'secret-payload');
$encrypted = $jwe->encrypt($keySet);
$decrypted = \SimpleJWT\JWE::decrypt($encrypted, $keySet, 'PBES2-HS256+A128KW');
Use for confidential claims (e.g., PII in tokens).
Middleware for JWT Validation:
use SimpleJWT\JWT;
class AuthenticateJWT
{
public function handle($request, Closure $next)
{
$token = $request->bearerToken();
if (!$token) abort(401);
try {
$decoded = JWT::decode($token, $this->keySet, 'HS256');
$request->merge(['user' => $decoded->getClaims()]);
} catch (\Exception $e) {
abort(401);
}
return $next($request);
}
}
Service Provider for Key Management:
class JWTServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton('jwt.keySet', function () {
return KeySet::createFromSecret(config('jwt.secret'));
});
}
}
API Resource Responses:
return response()->json([
'data' => $user,
'access_token' => (new JWT(['alg' => 'HS256'], [
'sub' => $user->id,
'exp' => time() + 3600
])->encode($this->keySet)),
]);
Algorithm Mismatch:
InvalidTokenException with SIGNATURE_VERIFICATION_ERROR.alg in the token matches the one passed to decode().
Example: JWT::decode($token, $keySet, 'HS256') must match the token’s alg.Key ID (kid) Handling:
kid is missing or mismatched.kid in keys or use KeySet::add($key, true) to auto-generate it.SimpleJWT\Keys\Key::getKeyId() to verify kid values.Clock Skew:
nbf (Not Before) claims and adjust server time or add a leeway buffer:
$claims['exp'] = time() + 3600;
$claims['nbf'] = time() - 60; // Allow 1-minute leeway
PEM vs. JWK:
RSAKey fails to load from PEM files.$key = new \SimpleJWT\Keys\RSAKey(file_get_contents('private.pem'), 'pem');
PHP Extensions:
SodiumException or RuntimeException during EdDSA/X25519 operations.sodium extension in php.ini or Dockerfile:
extension=sodium
Deserialize Without Validation:
$result = JWT::deserialise($token);
print_r($result['claims']); // Inspect claims
print_r($result['signatures']); // Inspect signatures
Key Validation:
try {
$keySet->validateKey($token, 'HS256');
} catch (\SimpleJWT\KeyException $e) {
// Key is invalid or unsupported
}
Algorithm Support:
SimpleJWT\Algorithms.PBES2-HS256+A128KW requires openssl and gmp.KeySet Caching:
$keySet = Cache::remember('jwt.keys', 3600, function () {
return KeySet::createFromSecret(config('jwt.secret'));
});
Avoid Re-encoding:
JWT objects for multiple encodes if claims/headers are static.JWE Optimization:
A256GCM) for performance-critical paths.Custom Claims Validation:
$decoded = JWT::decode($token, $keySet, 'HS256');
if (!$decoded->getClaim('role') === 'admin') {
abort(403);
}
Multi-Recipient JWE:
$jwe = new \SimpleJWT\JWE(['alg' => 'ECDH-ES+A128KW', 'enc' => 'A128GCM'], 'secret');
$encrypted = $jwe->encrypt($key
How can I help you explore Laravel packages today?