cube43 / slim-jwt-auth
PSR-7 and PSR-15 JWT Authentication Middleware
Installs: 7 007
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 0
Forks: 144
pkg:composer/cube43/slim-jwt-auth
Requires
- php: ~8.1.0 || ~8.2.0 || ~8.3.0
- lcobucci/jwt: ^5.1
- psr/http-message: ^1.1 || ^2.0
- psr/http-server-middleware: ^1.0.2
- psr/log: ^1.0|^2.0|^3.0
Requires (Dev)
- doctrine/coding-standard: ^12.0
- equip/dispatch: ^2.0
- infection/infection: ^0.27.7
- laminas/laminas-diactoros: ^3.3
- phpstan/phpstan: ^1.10.40
- phpunit/phpunit: ^10.4.2
- psalm/plugin-phpunit: ^0.18.4
- thecodingmachine/safe: ^2.5
- vimeo/psalm: ^5.15
This package is auto-updated.
Last update: 2026-02-08 15:10:58 UTC
README
PSR-7 and PSR-15 Middleware for JWT Authentication. This is a strict-typed, modern rewrite of the popular JWT middleware, designed for Slim Framework and other PSR-15 compliant frameworks.
Installation
Install via Composer:
composer require cube43/slim-jwt-auth
Usage
This library splits the JWT logic into two separate middlewares to provide better flexibility:
JwtAuthentication: Parses and validates the token. If valid, it attaches the decoded token to the request tokenAttributeNames. It does not block the request if the token is missing (it acts as a hydrator).JwtAuthentificationFirewall: Checks if the request requires authentication (based on rules). If it does, and no valid token was found by the previous middleware, it returns a 401 Unauthorized response.
Complete Example
Here is a complete example setup for Slim 4, including custom handlers and rules:
use Slim\Factory\AppFactory; use Tuupola\Middleware\JwtAuthentication; use Tuupola\Middleware\JwtAuthenticationOption; use Tuupola\Middleware\JwtAuthentificationFirewall; use Tuupola\Middleware\JwtAuthentication\FetchTokenFormHeader; use Tuupola\Middleware\JwtAuthentication\RequestPathRule; use Tuupola\Middleware\JwtAuthentificationUnAuthorizedHandler; use Tuupola\Middleware\JwtAuthentificationBeforeHandler; use Lcobucci\JWT\Signer\Key\InMemory; use Lcobucci\JWT\Token\Plain; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; use Laminas\Diactoros\Response; use Throwable; $app = AppFactory::create(); // 1. Configure Options $options = JwtAuthenticationOption::create(InMemory::plainText('super-secret-key')) ->withTokenAttributeName('jwt') ->withAllowedInsecureHosts(['localhost', '127.0.0.1']) ->withBeforeHandleRequestWhenTokenAvailable(new class implements JwtAuthentificationBeforeHandler { public function __invoke(ServerRequestInterface $request, Plain $token): ServerRequestInterface { assert($request->getAttribute('jwt') === $token); return $request->withAttribute('user_uuid', $token->claims()->get('uuid')); } }); // 2. Define Rules (e.g. protect /api, but allow /api/login) $pathRule = new RequestPathRule( path: ['/api'], ignore: ['/api/login', '/api/token'] ); // 3. Define Unauthorized Handler (JSON response) $unauthorizedHandler = new class implements JwtAuthentificationUnAuthorizedHandler { public function __invoke(ServerRequestInterface $request, ResponseInterface $response, Throwable $exception): ResponseInterface { $response->getBody()->write(json_encode(['status' => 'error', 'message' => 'Unauthorized'])); return $response->withHeader('Content-Type', 'application/json'); } }; // 4. Add Middleware (LIFO: Add Firewall first, then Authentication) // Firewall: Checks rules and blocks if no token is present when required // It runs LAST in the stack (executed AFTER Authentication) $app->add(new JwtAuthentificationFirewall( $options, new Response(), $unauthorizedHandler, $pathRule )); // Authentication: Extracts and decodes token // It runs FIRST in the stack (executed BEFORE Firewall) $app->add(JwtAuthentication::create( $options, new FetchTokenFormHeader() )); $app->run();
Important: In Slim, middleware is executed Last-In-First-Out. You must ensure
JwtAuthenticationruns beforeJwtAuthentificationFirewallso the token is available when the firewall checks for it.
Configuration Options
The JwtAuthenticationOption class uses a fluent interface for configuration.
$options = JwtAuthenticationOption::create(InMemory::plainText('secret')) ->withTokenAttributeName('jwt') // Attribute name for the decoded token ->withEnforceHttps(true) // Require HTTPS ->withAllowedInsecureHosts(['localhost']); // Allow HTTP on these hosts
Token Extraction
You define how the token is extracted when creating the JwtAuthentication middleware. You can pass multiple extractors.
use Tuupola\Middleware\JwtAuthentication\FetchTokenFormHeader; use Tuupola\Middleware\JwtAuthentication\FetchTokenFormCookie; $app->add(JwtAuthentication::create( $options, new FetchTokenFormHeader('Authorization', '/Bearer\s+(.*)$/i'), new FetchTokenFormCookie('auth_token') ));
Firewall Rules
To define which requests require authentication, pass RuleInterface implementations to the JwtAuthentificationFirewall constructor.
Path Rule
Restrict authentication to specific paths, or ignore specific paths.
use Tuupola\Middleware\JwtAuthentication\RequestPathRule; // Authenticate everything under /api, but ignore /api/login $pathRule = new RequestPathRule( path: ['/api'], ignore: ['/api/login'] ); $app->add(new JwtAuthentificationFirewall( $options, $response, new NullUnAuthorizedHandler(), // Default handler $pathRule ));
Method Rule
Ignore specific HTTP methods (e.g., OPTIONS).
use Tuupola\Middleware\JwtAuthentication\IgnoreHttpMethodRule; $methodRule = new IgnoreHttpMethodRule(['OPTIONS']); $app->add(new JwtAuthentificationFirewall( $options, $response, new NullUnAuthorizedHandler(), $pathRule, $methodRule ));
Handlers
You can customize behavior using handlers.
Before Handler
Modify the request after the token is decoded but before the next middleware.
use Tuupola\Middleware\JwtAuthentificationBeforeHandler; use Lcobucci\JWT\Token\Plain; $options = $options->withBeforeHandleRequestWhenTokenAvailable(new class implements JwtAuthentificationBeforeHandler { public function __invoke(ServerRequestInterface $request, Plain $token): ServerRequestInterface { assert($request->getAttribute('jwt') === $token); return $request->withAttribute('user_id', $token->claims()->get('uid')); } });
After Handler
Modify the response before returning it.
use Tuupola\Middleware\JwtAuthentificationAfterHandler; $options = $options->withAfterHandleRequestWhenTokenAvailable(new class implements JwtAuthentificationAfterHandler { public function __invoke(ResponseInterface $response, Plain $token): ResponseInterface { return $response->withHeader('X-Auth-Success', 'true'); } });
Unauthorized Handler
Customize the response when authentication fails (used by the Firewall).
use Tuupola\Middleware\JwtAuthentificationUnAuthorizedHandler; $firewall = new JwtAuthentificationFirewall( $options, $response, new class implements JwtAuthentificationUnAuthorizedHandler { public function __invoke(ServerRequestInterface $request, ResponseInterface $response, Throwable $exception): ResponseInterface { $response->getBody()->write(json_encode(['error' => 'Unauthorized'])); return $response->withHeader('Content-Type', 'application/json'); } } );
Security
By default, the middleware throws a RuntimeException if you attempt to use it over HTTP (insecure). To allow HTTP for development, use withAllowedInsecureHosts:
$options = $options->withAllowedInsecureHosts(['localhost', '127.0.0.1']);