PHPackages                             dev-toolbelt/jwt-token-manager - 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. dev-toolbelt/jwt-token-manager

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

dev-toolbelt/jwt-token-manager
==============================

Framework-agnostic JWT token manager with RSA/HMAC support

1.0.0(3mo ago)111671MITPHPPHP ^8.1CI passing

Since Feb 1Pushed 3mo agoCompare

[ Source](https://github.com/Dev-Toolbelt/jwt-token-manager)[ Packagist](https://packagist.org/packages/dev-toolbelt/jwt-token-manager)[ RSS](/packages/dev-toolbelt-jwt-token-manager/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (8)Versions (2)Used By (0)

JWT Token Manager
=================

[](#jwt-token-manager)

[![CI](https://github.com/dev-toolbelt/jwt-token-manager/actions/workflows/ci.yml/badge.svg)](https://github.com/dev-toolbelt/jwt-token-manager/actions/workflows/ci.yml)[![Coverage](https://camo.githubusercontent.com/748ca75c01528d6951ab38545bb80c182330a826f27af57ce2fc198236fbf16e/68747470733a2f2f636f6465636f762e696f2f67682f6465762d746f6f6c62656c742f6a77742d746f6b656e2d6d616e616765722f6272616e63682f6d61696e2f67726170682f62616467652e737667)](https://codecov.io/gh/dev-toolbelt/jwt-token-manager)[![Latest Stable Version](https://camo.githubusercontent.com/b91ec25f8f36f4521bdfdad6d278acb54cc52631234148cc768485ea4af4c733/68747470733a2f2f706f7365722e707567782e6f72672f6465762d746f6f6c62656c742f6a77742d746f6b656e2d6d616e616765722f762f737461626c65)](https://packagist.org/packages/dev-toolbelt/jwt-token-manager)[![Total Downloads](https://camo.githubusercontent.com/c79d6a440b73288b1771f65d378d785eff1ec8571a34aa6a2562afcc6b0bb5a3/68747470733a2f2f706f7365722e707567782e6f72672f6465762d746f6f6c62656c742f6a77742d746f6b656e2d6d616e616765722f646f776e6c6f616473)](https://packagist.org/packages/dev-toolbelt/jwt-token-manager)[![License](https://camo.githubusercontent.com/176c326d2ac7d2d3ca713306474a1fb4d115c355d3befe741ced096008bafb60/68747470733a2f2f706f7365722e707567782e6f72672f6465762d746f6f6c62656c742f6a77742d746f6b656e2d6d616e616765722f6c6963656e7365)](https://packagist.org/packages/dev-toolbelt/jwt-token-manager)[![PHP Version](https://camo.githubusercontent.com/fec3abc9aa556a186ffcde67f18f6e4f5fd036cdcfb41594dd2c87216da554a9/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f6465762d746f6f6c62656c742f6a77742d746f6b656e2d6d616e61676572)](https://packagist.org/packages/dev-toolbelt/jwt-token-manager)

A **framework-agnostic** PHP library for encoding, decoding, and validating JSON Web Tokens (JWT) with support for RSA, HMAC, ECDSA, and EdDSA algorithms.

Built with simplicity and security in mind, this package provides an easy way to manage JWT tokens without coupling your application to any specific framework.

Features
--------

[](#features)

- **Framework Agnostic** - Use with Laravel, Symfony, Yii, Slim, or any PHP application
- **Multiple Algorithms** - Support for HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512, PS256, PS384, PS512, and EdDSA
- **Type-Safe Configuration** - Strongly typed `Algorithm` enum and configuration objects
- **Protected Claims** - Critical claims (iss, sub, iat, exp, jti, sid) cannot be overridden for security
- **Flexible Validation** - Configurable required claims and audience validation
- **Session Tracking** - Built-in session ID (sid) and JWT ID (jti) generation using UUID v7
- **Comprehensive Exceptions** - Specific exceptions for expired, invalid, and malformed tokens
- **Timezone Support** - Type-safe timezone configuration with a comprehensive `Timezone` enum covering all PHP supported timezones

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

[](#requirements)

- PHP 8.1 or higher
- OpenSSL extension (for RSA/ECDSA algorithms)

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

[](#installation)

Install via Composer:

```
composer require dev-toolbelt/jwt-token-manager
```

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

[](#quick-start)

### 1. Generate RSA Keys (if you don't have them)

[](#1-generate-rsa-keys-if-you-dont-have-them)

Before using the library, you'll need an RSA key pair for signing and verifying tokens. If you already have keys, skip to step 2.

#### Linux / macOS

[](#linux--macos)

Open your terminal and run:

```
# Generate private key (2048-bit)
openssl genpkey -algorithm RSA -out private.key -pkeyopt rsa_keygen_bits:2048

# Extract public key
openssl rsa -pubout -in private.key -out public.key
```

#### Windows

[](#windows)

**Option 1: Using Git Bash (recommended)**

If you have Git installed, open Git Bash and use the same commands as Linux/macOS:

```
openssl genpkey -algorithm RSA -out private.key -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in private.key -out public.key
```

**Option 2: Using WSL (Windows Subsystem for Linux)**

```
wsl openssl genpkey -algorithm RSA -out private.key -pkeyopt rsa_keygen_bits:2048
wsl openssl rsa -pubout -in private.key -out public.key
```

**Option 3: Using OpenSSL for Windows**

1. Download OpenSSL from [slproweb.com/products/Win32OpenSSL.html](https://slproweb.com/products/Win32OpenSSL.html)
2. Install and add to PATH
3. Open Command Prompt and run the same commands

#### Production Keys (4096-bit)

[](#production-keys-4096-bit)

For production environments, consider using stronger 4096-bit keys:

```
openssl genpkey -algorithm RSA -out private.key -pkeyopt rsa_keygen_bits:4096
openssl rsa -pubout -in private.key -out public.key
```

> **Security Note:** Keep your private key secure and never commit it to version control. Add `*.key` to your `.gitignore` file.

### 2. Basic Usage

[](#2-basic-usage)

```
use DevToolbelt\JwtTokenManager\JwtConfig;
use DevToolbelt\JwtTokenManager\JwtTokenManager;

// Create configuration
$config = new JwtConfig(
    privateKey: file_get_contents('/path/to/private.key'),
    publicKey: file_get_contents('/path/to/public.key'),
    issuer: 'https://api.yourapp.com'
);

// Initialize the manager
$manager = new JwtTokenManager($config);

// Generate a token
$token = $manager->encode('user-123', [
    'name' => 'John Doe',
    'role' => 'admin'
]);

// Decode and validate the token
$payload = $manager->decode($token);

echo $payload->getSubject();      // "user-123"
echo $payload->getClaim('name');  // "John Doe"
echo $payload->getClaim('role');  // "admin"
```

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

[](#configuration)

### Basic Configuration

[](#basic-configuration)

```
use DevToolbelt\JwtTokenManager\JwtConfig;
use DevToolbelt\Enums\Security\Algorithm;
use DevToolbelt\Enums\Locale\Timezone;

$config = new JwtConfig(
    privateKey: $privateKey,
    publicKey: $publicKey,
    issuer: 'https://api.yourapp.com',
    algorithm: Algorithm::RS256,        // Default: RS256
    ttlMinutes: 60,                     // Default: 60 (1 hour)
    refreshTtlMinutes: 20160,           // Default: 20160 (14 days)
    audience: ['https://app.yourapp.com'],
    requiredClaims: ['iss', 'sub', 'exp', 'iat', 'jti', 'typ'],
    timezone: Timezone::AMERICA_SAO_PAULO  // Default: UTC
);
```

### Using Factory Methods

[](#using-factory-methods)

```
// From key files
$config = JwtConfig::fromKeyFiles(
    privateKeyPath: '/path/to/private.key',
    publicKeyPath: '/path/to/public.key',
    issuer: 'https://api.yourapp.com'
);

// From array (useful for framework config files)
$config = JwtConfig::fromArray([
    'private_key' => $privateKey,
    'public_key' => $publicKey,
    'issuer' => 'https://api.yourapp.com',
    'algorithm' => 'RS256',
    'ttl_minutes' => 60,
    'audience' => ['https://app.yourapp.com'],
    'timezone' => 'America/Sao_Paulo'  // Or use Timezone enum
]);
```

### Supported Algorithms

[](#supported-algorithms)

AlgorithmTypeDescription`HS256`, `HS384`, `HS512`HMACSymmetric key algorithms`RS256`, `RS384`, `RS512`RSAAsymmetric RSA algorithms`ES256`, `ES384`, `ES512`ECDSAElliptic Curve algorithms`PS256`, `PS384`, `PS512`RSA-PSSRSA with PSS padding`EdDSA`EdDSAEdwards-curve Digital Signature> **Why RS256 is the default?** RS256 (RSA with SHA-256) is the most widely adopted algorithm in the industry, offering an excellent balance between security and performance. It uses asymmetric keys, allowing you to share the public key for verification while keeping the private key secure. This makes it ideal for distributed systems and microservices architectures.

```
use DevToolbelt\Enums\Security\Algorithm;

// Check algorithm properties
Algorithm::RS256->isAsymmetric();  // true
Algorithm::HS256->isSymmetric();   // true
Algorithm::RS256->isRSA();         // true
Algorithm::ES256->isECDSA();       // true
```

### Timezone Configuration

[](#timezone-configuration)

The library provides a type-safe `Timezone` enum with all PHP supported timezones. The default timezone is **UTC**.

```
use DevToolbelt\Enums\Locale\Timezone;

// Using the enum directly
$config = new JwtConfig(
    privateKey: $privateKey,
    publicKey: $publicKey,
    issuer: 'https://api.yourapp.com',
    timezone: Timezone::AMERICA_SAO_PAULO
);

// Available timezone helper methods
Timezone::AMERICA_NEW_YORK->toDateTimeZone();    // Returns DateTimeZone instance
Timezone::EUROPE_LONDON->getUtcOffset();         // Returns offset in seconds
Timezone::ASIA_TOKYO->getUtcOffsetString();      // Returns "+09:00"

// Create from string (useful for config files)
$timezone = Timezone::from('America/Sao_Paulo');
$timezone = Timezone::tryFrom('Invalid/Zone');   // Returns null for invalid zones
```

Common timezone examples:

- **Americas**: `AMERICA_NEW_YORK`, `AMERICA_LOS_ANGELES`, `AMERICA_CHICAGO`, `AMERICA_SAO_PAULO`
- **Europe**: `EUROPE_LONDON`, `EUROPE_PARIS`, `EUROPE_BERLIN`, `EUROPE_MADRID`
- **Asia**: `ASIA_TOKYO`, `ASIA_SHANGHAI`, `ASIA_SINGAPORE`, `ASIA_DUBAI`
- **UTC**: `UTC`

> **Note**: JWT timestamps (`iat`, `exp`, `nbf`) are always Unix timestamps (seconds since Unix epoch), which are timezone-agnostic. The timezone configuration is used internally for consistent `DateTimeImmutable` operations and can be useful for logging, debugging, and future enhancements.

Usage
-----

[](#usage)

### Generating Tokens

[](#generating-tokens)

```
$manager = new JwtTokenManager($config);

// Simple token with just a subject
$token = $manager->encode('user-123');

// Token with custom claims
$token = $manager->encode('user-123', [
    'name' => 'John Doe',
    'email' => 'john@example.com',
    'roles' => ['admin', 'editor'],
    'tenant_id' => 'tenant-456'
]);

// Get the generated session ID and JWT ID
$sessionId = $manager->getLastSessionId();  // UUID v7
$jti = $manager->getLastJti();              // UUID v7
```

### Token Claims

[](#token-claims)

The generated token includes these standard claims:

ClaimDescriptionCustomizable`iss`Issuer (from config)No`sub`Subject (user identifier)No`aud`Audience (from config)Yes`iat`Issued at timestampNo`exp`Expiration timestampNo`nbf`Not before timestampYes`jti`Unique JWT ID (UUID v7)No`sid`Session ID (UUID v7)No`typ`Token type (default: "access")Yes> **Note**: Claims marked as **Customizable: No** are automatically generated and managed by the library to ensure token integrity and security. You cannot override these values.

### Decoding Tokens

[](#decoding-tokens)

Below is a comprehensive example showing all possible exceptions that can be thrown during token decoding and validation:

```
try {
    $payload = $manager->decode($token);

    // Standard claim accessors
    $payload->getSubject();      // sub claim
    $payload->getIssuer();       // iss claim
    $payload->getAudience();     // aud claim (array)
    $payload->getExpiration();   // exp claim (timestamp)
    $payload->getIssuedAt();     // iat claim (timestamp)
    $payload->getNotBefore();    // nbf claim (timestamp)
    $payload->getJti();          // jti claim
    $payload->getSessionId();    // sid claim
    $payload->getType();         // typ claim

    // Custom claims
    $payload->getClaim('role');  // Get any custom claim
    $payload->hasClaim('role');  // Check if claim exists

    // Get all claims as array
    $allClaims = $payload->toArray();

    // Check expiration
    if ($payload->isExpired()) {
        // Token has expired
    }

} catch (ExpiredTokenException $e) {
    // Token has expired
} catch (InvalidSignatureException $e) {
    // Public key could not validate the token signature
} catch (InvalidTokenException $e) {
    // Token is malformed or not yet valid
} catch (InvalidClaimException $e) {
    // A claim validation failed
    $e->getClaimName();      // Which claim failed
    $e->getActualValue();    // What value was received
    $e->getExpectedValue();  // What was expected
} catch (MissingClaimsException $e) {
    // Required claims are missing
    $e->getMissingClaims();  // Array of missing claim names
}
```

### Refresh Tokens

[](#refresh-tokens)

While refresh tokens are not mandatory, implementing them is highly recommended for a secure authentication flow. The concept is simple: **access tokens should be short-lived** (minutes to hours) to minimize the impact if compromised, while **refresh tokens are long-lived** (days to weeks) and used solely to obtain new access tokens. This approach reduces the attack window for stolen tokens while maintaining a smooth user experience without frequent re-authentication.

```
// Generate a refresh token
$refreshToken = $manager->generateRefreshToken();  // SHA1 hash

// Get TTL values
$accessTtl = $manager->getTokenTtl();        // In seconds
$refreshTtl = $manager->getRefreshTokenTtl(); // In seconds
```

> **Best Practice**: Store refresh tokens securely (e.g., in a database with the user association) and invalidate them when the user logs out or when suspicious activity is detected.

### Overriding Optional Claims

[](#overriding-optional-claims)

Some claims can be overridden via custom claims for flexibility:

```
// Override the token type for refresh tokens
$refreshToken = $manager->encode('user-123', [
    'typ' => 'refresh'
]);

// Override not-before time
$token = $manager->encode('user-123', [
    'nbf' => time() + 3600  // Token valid in 1 hour
]);

// Override audience for specific consumers
$token = $manager->encode('user-123', [
    'aud' => ['https://mobile-app.yourapp.com']
]);
```

> **Note**: Protected claims (`iss`, `sub`, `iat`, `exp`, `jti`, `sid`) cannot be overridden for security reasons.

Framework Integration Examples
------------------------------

[](#framework-integration-examples)

### Laravel

[](#laravel)

```
// config/jwt.php
return [
    'private_key' => storage_path('keys/private.key'),
    'public_key' => storage_path('keys/public.key'),
    'issuer' => env('APP_URL'),
    'audience' => [env('FRONTEND_URL')],
    'ttl_minutes' => 60,
];

// app/Providers/AppServiceProvider.php
use DevToolbelt\JwtTokenManager\JwtConfig;
use DevToolbelt\JwtTokenManager\JwtTokenManager;

public function register(): void
{
    $this->app->singleton(JwtTokenManager::class, function ($app) {
        $config = JwtConfig::fromKeyFiles(
            privateKeyPath: config('jwt.private_key'),
            publicKeyPath: config('jwt.public_key'),
            issuer: config('jwt.issuer'),
            audience: config('jwt.audience'),
            ttlMinutes: config('jwt.ttl_minutes')
        );

        return new JwtTokenManager($config);
    });
}

// Usage in a controller or service
use DevToolbelt\JwtTokenManager\JwtTokenManager;

class AuthController extends Controller
{
    public function login(Request $request)
    {
        // Using dependency injection
        $manager = app(JwtTokenManager::class);

        // Or using the helper with type hint
        /** @var JwtTokenManager $manager */
        $manager = app()->make(JwtTokenManager::class);

        $token = $manager->encode($user->id, [
            'name' => $user->name,
            'email' => $user->email
        ]);

        return response()->json(['token' => $token]);
    }
}
```

### Symfony

[](#symfony)

```
# config/services.yaml
services:
    DevToolbelt\JwtTokenManager\JwtConfig:
        factory: ['DevToolbelt\JwtTokenManager\JwtConfig', 'fromKeyFiles']
        arguments:
            $privateKeyPath: '%kernel.project_dir%/config/keys/private.key'
            $publicKeyPath: '%kernel.project_dir%/config/keys/public.key'
            $issuer: '%env(APP_URL)%'

    DevToolbelt\JwtTokenManager\JwtTokenManager:
        arguments:
            $config: '@DevToolbelt\JwtTokenManager\JwtConfig'
```

```
// src/Controller/AuthController.php
namespace App\Controller;

use DevToolbelt\JwtTokenManager\JwtTokenManager;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;

class AuthController extends AbstractController
{
    public function __construct(
        private readonly JwtTokenManager $jwtManager
    ) {}

    #[Route('/api/login', name: 'api_login', methods: ['POST'])]
    public function login(): JsonResponse
    {
        $user = $this->getUser();

        $token = $this->jwtManager->encode($user->getId(), [
            'email' => $user->getEmail(),
            'roles' => $user->getRoles()
        ]);

        return $this->json(['token' => $token]);
    }
}
```

### Slim / PHP-DI

[](#slim--php-di)

```
// config/container.php
use DevToolbelt\JwtTokenManager\JwtConfig;
use DevToolbelt\JwtTokenManager\JwtTokenManager;
use Psr\Container\ContainerInterface;

return [
    JwtTokenManager::class => function (ContainerInterface $c) {
        $config = JwtConfig::fromKeyFiles(
            privateKeyPath: __DIR__ . '/../keys/private.key',
            publicKeyPath: __DIR__ . '/../keys/public.key',
            issuer: $_ENV['APP_URL']
        );

        return new JwtTokenManager($config);
    },
];
```

```
// src/Action/LoginAction.php
use DevToolbelt\JwtTokenManager\JwtTokenManager;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

class LoginAction
{
    public function __construct(
        private readonly JwtTokenManager $jwtManager
    ) {}

    public function __invoke(Request $request, Response $response): Response
    {
        $data = $request->getParsedBody();

        // After validating credentials...
        $token = $this->jwtManager->encode($userId, [
            'email' => $data['email']
        ]);

        $response->getBody()->write(json_encode(['token' => $token]));
        return $response->withHeader('Content-Type', 'application/json');
    }
}
```

### CodeIgniter 4

[](#codeigniter-4)

```
// app/Config/Services.php
namespace Config;

use CodeIgniter\Config\BaseService;
use DevToolbelt\JwtTokenManager\JwtConfig;
use DevToolbelt\JwtTokenManager\JwtTokenManager;

class Services extends BaseService
{
    public static function jwtManager(bool $getShared = true): JwtTokenManager
    {
        if ($getShared) {
            return static::getSharedInstance('jwtManager');
        }

        $config = JwtConfig::fromKeyFiles(
            privateKeyPath: WRITEPATH . 'keys/private.key',
            publicKeyPath: WRITEPATH . 'keys/public.key',
            issuer: base_url()
        );

        return new JwtTokenManager($config);
    }
}
```

```
// app/Controllers/Auth.php
namespace App\Controllers;

use Config\Services;

class Auth extends BaseController
{
    public function login()
    {
        $manager = Services::jwtManager();

        // After validating credentials...
        $token = $manager->encode($user->id, [
            'email' => $user->email,
            'name' => $user->name
        ]);

        return $this->response->setJSON(['token' => $token]);
    }
}
```

### CakePHP 5

[](#cakephp-5)

```
// config/services.php
use Cake\Core\Container;
use Cake\Core\ServiceProvider;
use DevToolbelt\JwtTokenManager\JwtConfig;
use DevToolbelt\JwtTokenManager\JwtTokenManager;

class JwtServiceProvider extends ServiceProvider
{
    protected array $provides = [JwtTokenManager::class];

    public function services(Container $container): void
    {
        $container->addShared(JwtTokenManager::class, function () {
            $config = JwtConfig::fromKeyFiles(
                privateKeyPath: CONFIG . 'keys/private.key',
                publicKeyPath: CONFIG . 'keys/public.key',
                issuer: env('APP_URL', 'https://localhost')
            );

            return new JwtTokenManager($config);
        });
    }
}

// In src/Application.php, register the provider:
// $container->addServiceProvider(new JwtServiceProvider());
```

```
// src/Controller/AuthController.php
namespace App\Controller;

use DevToolbelt\JwtTokenManager\JwtTokenManager;

class AuthController extends AppController
{
    public function login()
    {
        $manager = $this->getContainer()->get(JwtTokenManager::class);

        // After validating credentials...
        $token = $manager->encode($user->id, [
            'email' => $user->email
        ]);

        return $this->response
            ->withType('application/json')
            ->withStringBody(json_encode(['token' => $token]));
    }
}
```

### Yii2

[](#yii2)

```
// config/web.php
return [
    'components' => [
        'jwt' => [
            'class' => 'app\components\JwtComponent',
        ],
    ],
];
```

```
// components/JwtComponent.php
namespace app\components;

use DevToolbelt\JwtTokenManager\JwtConfig;
use DevToolbelt\JwtTokenManager\JwtTokenManager;
use yii\base\Component;

class JwtComponent extends Component
{
    private ?JwtTokenManager $manager = null;

    public function init(): void
    {
        parent::init();

        $config = JwtConfig::fromKeyFiles(
            privateKeyPath: \Yii::getAlias('@app/config/keys/private.key'),
            publicKeyPath: \Yii::getAlias('@app/config/keys/public.key'),
            issuer: \Yii::$app->params['appUrl']
        );

        $this->manager = new JwtTokenManager($config);
    }

    public function getManager(): JwtTokenManager
    {
        return $this->manager;
    }
}
```

```
// controllers/AuthController.php
namespace app\controllers;

use yii\rest\Controller;

class AuthController extends Controller
{
    public function actionLogin()
    {
        $manager = \Yii::$app->jwt->getManager();

        // After validating credentials...
        $token = $manager->encode($user->id, [
            'email' => $user->email,
            'name' => $user->name
        ]);

        return ['token' => $token];
    }
}
```

Testing
-------

[](#testing)

```
# Run all tests
composer test

# Run with coverage
composer test:coverage

# Code quality
composer phpcs        # Check code style
composer phpcs:fix    # Fix code style
composer phpstan      # Static analysis
```

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

[](#contributing)

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

Please make sure to update tests as appropriate and follow the existing code style.

Security
--------

[](#security)

If you discover any security-related issues, please email  instead of using the issue tracker.

Credits
-------

[](#credits)

- [Kilderson Sena](https://github.com/dersonsena)
- [All Contributors](../../contributors)

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

[](#contributing-1)

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

Please make sure to update tests as appropriate and follow PSR-12 coding standards.

### Code Quality Requirements

[](#code-quality-requirements)

CheckToolRequirementTestsPHPUnitAll tests must passCoveragePCOVMinimum **85%** coverageCode StylePHP CodeSnifferPSR-12 compliantStatic AnalysisPHPStanLevel 6, no errors### Pull Request Guidelines

[](#pull-request-guidelines)

Before submitting a PR, ensure:

1. **All tests pass:** `composer test`
2. **Coverage is at least 85%:** `composer test:coverage`
3. **Code style is correct:** `composer phpcs`
4. **No static analysis errors:** `composer phpstan`

> **Important:** Pull requests with coverage below 85% will be automatically blocked by CI.

### Coverage Report

[](#coverage-report)

- **Dashboard:** [Codecov](https://codecov.io/gh/dev-toolbelt/jwt-token-manager)
- **HTML Report:** [GitHub Pages](https://dev-toolbelt.github.io/jwt-token-manager/)

License
-------

[](#license)

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

---

Made with ❤️ by [Dev Toolbelt](https://github.com/Dev-Toolbelt)

###  Health Score

41

—

FairBetter than 89% of packages

Maintenance80

Actively maintained with recent releases

Popularity23

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity43

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

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

Unknown

Total

1

Last Release

105d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/61bcbe92662af44dfdd25b210c73e52956e381536edb62ddfae95810e3333e31?d=identicon)[astrotechdevs](/maintainers/astrotechdevs)

---

Top Contributors

[![dersonsena](https://avatars.githubusercontent.com/u/9482515?v=4)](https://github.com/dersonsena "dersonsena (11 commits)")

---

Tags

agnostic-to-frameworksapiauthenticationauthentication-backendhmacjwtjwt-authenticationjwt-tokenrsatokenjwtrsaAuthenticationtokenhmac

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/dev-toolbelt-jwt-token-manager/health.svg)

```
[![Health](https://phpackages.com/badges/dev-toolbelt-jwt-token-manager/health.svg)](https://phpackages.com/packages/dev-toolbelt-jwt-token-manager)
```

###  Alternatives

[admad/cakephp-jwt-auth

CakePHP plugin for authenticating using JSON Web Tokens

160680.3k8](/packages/admad-cakephp-jwt-auth)[bizley/jwt

JWT integration for Yii 2

67425.3k2](/packages/bizley-jwt)[tuupola/branca

Authenticated and encrypted API tokens using modern crypto.

52309.2k1](/packages/tuupola-branca)[internacionalweb/cognito-token-verifier

This library verifies that the signature of the JWT is valid, comes from a desired application, and that the token has not been tampered with or expired.

102.1k](/packages/internacionalweb-cognito-token-verifier)

PHPackages © 2026

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