PHPackages                             methorz/jwt-auth-middleware - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Authentication &amp; Authorization](/categories/authentication)
4. /
5. methorz/jwt-auth-middleware

ActiveLibrary[Authentication &amp; Authorization](/categories/authentication)

methorz/jwt-auth-middleware
===========================

PSR-15 JWT authentication middleware with zero-config approach. Supports HS256/RS256, Bearer/custom headers, optional user loading, and scope validation.

v1.1.0(4mo ago)048MITPHPPHP ^8.2CI passing

Since Dec 15Pushed 4mo agoCompare

[ Source](https://github.com/MethorZ/jwt-auth-middleware)[ Packagist](https://packagist.org/packages/methorz/jwt-auth-middleware)[ GitHub Sponsors](https://github.com/methorz)[ RSS](/packages/methorz-jwt-auth-middleware/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (12)Versions (3)Used By (0)

JWT Authentication Middleware
=============================

[](#jwt-authentication-middleware)

[![CI](https://github.com/methorz/jwt-auth-middleware/actions/workflows/ci.yml/badge.svg)](https://github.com/methorz/jwt-auth-middleware/actions/workflows/ci.yml)[![codecov](https://camo.githubusercontent.com/d9cdce20ff399517c85abe8f41eab4835fb3215bf8556fd2e8c112e280b98b81/68747470733a2f2f636f6465636f762e696f2f67682f6d6574686f727a2f6a77742d617574682d6d6964646c65776172652f6272616e63682f6d61696e2f67726170682f62616467652e737667)](https://codecov.io/gh/methorz/jwt-auth-middleware)[![PHPStan](https://camo.githubusercontent.com/3d63a7e6e05ca0dafbdd1cb2da881539a5ccd543deb25f6d8d1174cd11815bfe/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d6c6576656c2532306d61782d627269676874677265656e2e7376673f7374796c653d666c6174)](https://phpstan.org/)[![PHP Version](https://camo.githubusercontent.com/c9f64f714c636ba27a3bba6dfd52f98426832db1262747efa54b212d16943651/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253545382e322d626c7565)](https://www.php.net/)[![License](https://camo.githubusercontent.com/7013272bd27ece47364536a221edb554cd69683b68a46fc0ee96881174c4214c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667)](LICENSE)

PSR-15 JWT authentication middleware with **zero-config approach**. Just add your JWT secret and you're ready to go!

Features
--------

[](#features)

- ✅ **Zero Configuration** - Works out of the box with environment variables
- ✅ **PSR-15 Compliant** - Standard middleware interface
- ✅ **Framework Agnostic** - Works with any PSR-15 compatible framework
- ✅ **Flexible Token Extraction** - Bearer token (default) or custom header
- ✅ **Multiple Algorithms** - Supports HS256 (symmetric) and RS256 (asymmetric)
- ✅ **Standard Claims Validation** - Validates exp, nbf, iss, aud automatically
- ✅ **Optional User Loading** - Inject your own user loader
- ✅ **Optional Scope Validation** - Route-level permission checks
- ✅ **Route-Specific Protection** - Opt-in authentication per route

Installation
------------

[](#installation)

```
composer require methorz/jwt-auth-middleware
```

Quick Start
-----------

[](#quick-start)

### 1. Set Environment Variable

[](#1-set-environment-variable)

```
# For symmetric key (HS256)
JWT_SECRET=your-secret-key-here

# Or for asymmetric (RS256)
JWT_PUBLIC_KEY_PATH=/path/to/public.pem
JWT_ALGORITHM=RS256
```

### 2. Add to Your Pipeline

[](#2-add-to-your-pipeline)

**Mezzio:**

```
// config/pipeline.php
$app->pipe(\MethorZ\JwtAuthMiddleware\Middleware\JwtAuthenticationMiddleware::class);
```

**Slim:**

```
$app->add(\MethorZ\JwtAuthMiddleware\Middleware\JwtAuthenticationMiddleware::class);
```

### 3. Protect Routes

[](#3-protect-routes)

**Mezzio:**

```
$app->get('/api/protected', ProtectedHandler::class, 'protected-route')
    ->setOptions(['auth' => ['required' => true]]);
```

**In Your Handler:**

```
public function handle(ServerRequestInterface $request): ResponseInterface
{
    // Get JWT claims from request
    $claims = $request->getAttribute('jwt_claims');
    $userId = $claims->get('sub'); // User ID from token

    // Optional: Get loaded user (if user loader configured)
    $user = $request->getAttribute('user');

    return new JsonResponse(['user_id' => $userId]);
}
```

That's it! 🎉

Configuration
-------------

[](#configuration)

### Environment Variables (Zero-Config)

[](#environment-variables-zero-config)

VariableDefaultDescription`JWT_SECRET`-Secret key for HS256 (required if using symmetric)`JWT_PUBLIC_KEY_PATH`-Path to public key for RS256 (required if using asymmetric)`JWT_ALGORITHM``HS256`Algorithm: `HS256` or `RS256``JWT_ISSUER`-Expected `iss` claim (optional)`JWT_AUDIENCE`-Expected `aud` claim (optional)`JWT_HEADER_NAME``Authorization`Header to extract token from`JWT_HEADER_PREFIX``Bearer`Token prefix (e.g., "Bearer ")### Advanced Configuration (Optional)

[](#advanced-configuration-optional)

If you need more control, create a configuration file:

```
// config/autoload/jwt-auth.php
return [
    'jwt_auth' => [
        'algorithm' => 'HS256',
        'secret' => $_ENV['JWT_SECRET'],

        // Validation constraints
        'issuer' => 'your-app',
        'audience' => 'your-api',

        // Token extraction
        'header_name' => 'Authorization',
        'header_prefix' => 'Bearer',

        // Optional: Custom header
        // 'header_name' => 'X-Auth-Token',
        // 'header_prefix' => '',

        // Optional: User loader service
        // 'user_loader' => UserLoaderInterface::class,
    ],
];
```

Usage Examples
--------------

[](#usage-examples)

### Protect Specific Routes

[](#protect-specific-routes)

```
// Only require JWT for specific routes
$app->get('/public', PublicHandler::class); // No auth

$app->get('/protected', ProtectedHandler::class)
    ->setOptions(['auth' => ['required' => true]]); // Requires JWT
```

### Scope/Permission Validation

[](#scopepermission-validation)

```
// Require specific scopes
$app->post('/admin/users', AdminHandler::class)
    ->setOptions([
        'auth' => [
            'required' => true,
            'scopes' => ['admin', 'users:write'],
        ],
    ]);
```

In your token, include a `scope` claim:

```
{
  "sub": "user123",
  "scope": "admin users:write users:read"
}
```

### Custom User Loading

[](#custom-user-loading)

Implement `UserLoaderInterface` to load user from database:

```
use MethorZ\JwtAuthMiddleware\Contract\UserLoaderInterface;
use Lcobucci\JWT\Token\Plain;

class DatabaseUserLoader implements UserLoaderInterface
{
    public function __construct(private UserRepository $repository) {}

    public function loadUser(Plain $token): ?object
    {
        $userId = $token->claims()->get('sub');
        return $this->repository->findById($userId);
    }
}
```

Register in container:

```
// ConfigProvider.php
use MethorZ\JwtAuthMiddleware\Contract\UserLoaderInterface;

return [
    'dependencies' => [
        'factories' => [
            UserLoaderInterface::class => DatabaseUserLoaderFactory::class,
        ],
    ],
];
```

Now `$request->getAttribute('user')` contains your user object!

### Generate Tokens (Separate from Middleware)

[](#generate-tokens-separate-from-middleware)

```
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;

$config = Configuration::forSymmetricSigner(
    new Sha256(),
    InMemory::plainText($_ENV['JWT_SECRET'])
);

$now = new DateTimeImmutable();
$token = $config->builder()
    ->issuedBy('your-app')
    ->permittedFor('your-api')
    ->identifiedBy('unique-token-id')
    ->issuedAt($now)
    ->expiresAt($now->modify('+1 hour'))
    ->withClaim('sub', 'user123')
    ->withClaim('scope', 'users:read users:write')
    ->getToken($config->signer(), $config->signingKey());

echo $token->toString(); // Send to client
```

Request Attributes
------------------

[](#request-attributes)

After successful authentication, these attributes are added to the request:

AttributeTypeDescription`jwt_token``Lcobucci\JWT\Token\Plain`Full parsed token`jwt_claims``Lcobucci\JWT\Token\DataSet`Token claims`user``objectnull`Exceptions
----------

[](#exceptions)

All exceptions extend `MethorZ\JwtAuthMiddleware\Exception\JwtAuthenticationException`:

ExceptionWhen Thrown`MissingTokenException`No token in request`InvalidTokenException`Token is malformed`ExpiredTokenException`Token has expired`InvalidSignatureException`Signature verification failed`InsufficientScopeException`Required scope missing**Recommendation:** Use with [`methorz/http-problem-details`](https://github.com/methorz/http-problem-details) to automatically format exceptions as RFC 7807 Problem Details responses.

Testing
-------

[](#testing)

```
composer test          # Run tests
composer cs-check      # Check code style
composer cs-fix        # Fix code style
composer analyze       # Static analysis
composer check         # All checks
```

Requirements
------------

[](#requirements)

- PHP 8.2 or higher
- PSR-15 compatible framework
- lcobucci/jwt ^5.4

License
-------

[](#license)

MIT License. See [LICENSE](LICENSE) for details.

Contributing
------------

[](#contributing)

Contributions welcome! Please ensure:

- All tests pass
- Code follows PSR-12
- PHPStan level 9 passes
- Zero-config principle maintained

###  Health Score

36

—

LowBetter than 82% of packages

Maintenance74

Regular maintenance activity

Popularity8

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~6 days

Total

2

Last Release

148d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/b0233a9072fdc1746031fd96b487fce9b0d5f55c0124b70fc51ac2bb833bb906?d=identicon)[MethorZ](/maintainers/MethorZ)

---

Tags

api-securityauthenticationauthorizationbearer-tokenframework-agnostichttp-middlewarejwtjwt-authenticationlcobucci-jwtmiddlewarephpphp8psr-15scope-validationsecurityzero-config

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/methorz-jwt-auth-middleware/health.svg)

```
[![Health](https://phpackages.com/badges/methorz-jwt-auth-middleware/health.svg)](https://phpackages.com/packages/methorz-jwt-auth-middleware)
```

###  Alternatives

[cakephp/cakephp

The CakePHP framework

8.8k18.5M1.6k](/packages/cakephp-cakephp)[league/oauth2-server

A lightweight and powerful OAuth 2.0 authorization and resource server library with support for all the core specification grants. This library will allow you to secure your API with OAuth and allow your applications users to approve apps that want to access their data from your API.

6.6k136.0M248](/packages/league-oauth2-server)[thecodingmachine/graphqlite

Write your GraphQL queries in simple to write controllers (using webonyx/graphql-php).

5723.1M30](/packages/thecodingmachine-graphqlite)[scheb/2fa

Two-factor authentication for Symfony applications (please use scheb/2fa-bundle to install)

578630.7k1](/packages/scheb-2fa)[neos/flow

Flow Application Framework

862.0M451](/packages/neos-flow)[neos/flow-development-collection

Flow packages in a joined repository for pull requests.

144179.3k3](/packages/neos-flow-development-collection)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
