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(6mo ago)048MITPHPPHP ^8.2CI passing

Since Dec 15Pushed 6mo 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 today

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

34

—

LowBetter than 75% of packages

Maintenance67

Regular maintenance activity

Popularity8

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity49

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

196d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/900146?v=4)[MethorZ](/maintainers/MethorZ)[@MethorZ](https://github.com/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

[symfony/symfony

The Symfony PHP framework

31.4k87.2M2.2k](/packages/symfony-symfony)[cakephp/cakephp

The CakePHP framework

8.9k19.5M1.8k](/packages/cakephp-cakephp)[tempest/framework

The PHP framework that gets out of your way.

2.2k34.4k15](/packages/tempest-framework)[typo3/cms

TYPO3 CMS is a free open source Content Management Framework initially created by Kasper Skaarhoj and licensed under GNU/GPL.

1.2k1.9M122](/packages/typo3-cms)[mcp/sdk

Model Context Protocol SDK for Client and Server applications in PHP

1.5k1.5M89](/packages/mcp-sdk)[bref/bref

Bref is a framework to write and deploy serverless PHP applications on AWS Lambda.

3.4k10.6M67](/packages/bref-bref)

PHPackages © 2026

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