## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require web-token/jwt-framework
For Symfony projects, use the bundle:
composer require web-token/jwt-framework-bundle
First Use Case: Generate and verify a JWT token in a Laravel controller:
use WebToken\JWT\Builder;
use WebToken\JWT\Signature\Algorithm\HS256;
use WebToken\JWT\Signature\Key\InMemory;
// Create a key (store securely in production)
$key = new InMemory('your-secret-key');
// Build and sign a token
$builder = new Builder();
$token = $builder
->withPayload(['user_id' => 123, 'role' => 'admin'])
->withKey($key, new HS256())
->getToken();
// Verify and decode the token
$verifier = new Verifier($key, new HS256());
$decoded = $verifier->verify($token);
Where to Look First:
src/WebToken/JWT/ directory for core classestests/ for practical examples and edge cases$token = (new Builder())
->withPayload(['sub' => 'user123'])
->issuedAt(time())
->expiresAt(time() + 3600)
->withKey($key, new RS256())
->getToken();
AbstractClaim or use CustomClaim:
$claim = new CustomClaim('custom_claim', 'value');
$builder->withClaim($claim);
$verifier = new Verifier($key, new ES256());
$decoded = $verifier->verify($token, [
new ExpirationTimeChecker($clock),
new IssuedAtChecker($clock),
]);
CheckerInterface for business logic:
class RoleChecker implements CheckerInterface {
public function check(JWT $jwt): void {
if ($jwt->getPayload()['role'] !== 'admin') {
throw new \RuntimeException('Invalid role');
}
}
}
KeyStorageInterface (e.g., FileKeyStorage, RedisKeyStorage):
$storage = new FileKeyStorage('/path/to/keys');
$key = $storage->getKey('my-key-id');
KeyRotationStrategy for automated key updates.$jweBuilder = new JWEBuilder();
$encrypted = $jweBuilder
->withPlaintext('sensitive-data')
->withKeyEncryptionKey($keyEncryptionKey, new A256KW())
->withContentEncryptionKey($contentKey, new A256GCM())
->getToken();
$jweDecrypter = new JWEDecrypter();
$plaintext = $jweDecrypter
->withToken($encrypted)
->withKeyDecryptionKey($keyEncryptionKey, new A256KW())
->withContentDecryptionKey($contentKey, new A256GCM())
->getPlaintext();
namespace App\Http\Middleware;
use Closure;
use WebToken\JWT\Verification\Verifier;
class AuthenticateJWT {
public function handle($request, Closure $next) {
$token = $request->bearerToken();
$verifier = app(Verifier::class);
$decoded = $verifier->verify($token);
auth()->setUser($decoded->getPayload());
return $next($request);
}
}
public function register() {
$this->app->singleton(Verifier::class, function ($app) {
$key = new InMemory(config('jwt.secret'));
return new Verifier($key, new HS256());
});
}
$mockKey = $this->createMock(InMemory::class);
$mockKey->method('getKeyMaterial')->willReturn('mock-key');
$verifier = new Verifier($mockKey, new HS256());
$this->expectException(JWTException::class);
$verifier->verify('invalid.token');
$alg = $request->input('alg', 'HS256');
$algorithm = match ($alg) {
'RS256' => new RS256(),
'ES256' => new ES256(),
default => new HS256(),
};
$refreshToken = $builder
->withPayload(['jti' => 'refresh-123'])
->withKey($refreshKey, new HS512())
->issuedAt(time())
->expiresAt(time() + 86400) // 24h expiry
->getToken();
// Validate refresh token and issue new access token
$verifier->verify($refreshToken);
$accessToken = $builder
->withPayload(['sub' => 'user123'])
->withKey($accessKey, new HS256())
->getToken();
$builder->withHeader('custom_header', 'value');
$decoded = $verifier->verify($token);
$customHeader = $decoded->getHeader('custom_header');
$cache = new Psr6CacheAdapter(new FilesystemCacheStorage('/path/to/cache'));
$storage = new CachedKeyStorage($fileStorage, $cache);
if (extension_loaded('sodium')) {
Base64UrlSafe::setSodiumSupport(true);
}
HS256 for signing but verifying with RS256.try {
$verifier->verify($token);
} catch (JWTException $e) {
if ($e->getCode() === JWTException::INVALID_ALGORITHM) {
// Handle algorithm mismatch
}
}
$key = new InMemory(config('jwt.secret'));
KeyRotationStrategy.PSR-20 ClockInterface and allow a small leeway:
$clock = new SystemClock();
$checker = new ExpirationTimeChecker($clock, 60); // 1-minute leeway
JWE for large payloads or split data into multiple tokens.alg header.JWS with integrity-protected headers (enabled by default in v4.1+).Base64UrlSafe utilities:
use WebToken\Base64UrlSafe\Base64UrlSafe;
$decoded = Base64UrlSafe::decode($token);
How can I help you explore Laravel packages today?