daycry/jwt

JWT Token for Codeigniter 4

Maintainers

Package info

github.com/daycry/jwt

pkg:composer/daycry/jwt

Statistics

Installs: 6 329

Dependents: 4

Suggesters: 0

Stars: 1

Open Issues: 0

v3.0.0 2026-05-08 09:39 UTC

This package is auto-updated.

Last update: 2026-05-08 09:48:16 UTC


README

A JWT (JSON Web Token) library for CodeIgniter 4, built on top of lcobucci/jwt ^5. Supports HMAC, RSA and ECDSA.

Package

Latest Stable Version Total Downloads Monthly Downloads PHP Version Require License

Quality

PHP Tests PHPStan Psalm Rector Code Style CodeQL Coverage Status

Community

GitHub stars Donate

Requirements

  • PHP 8.2 or higher
  • CodeIgniter 4.x
  • lcobucci/jwt ^5.5

Upgrading from v2.x? Read the v2 → v3 migration guide.

Installation

composer require daycry/jwt

Publish the configuration file

php spark jwt:publish

Generate a signing key

php spark jwt:key

The key is written automatically to .env as jwt.signer. Use --show to print it without touching the file.

⚠️ Never commit .env to version control.

Quick Start

php spark jwt:publish     # write app/Config/JWT.php
php spark jwt:key         # generate jwt.signer in .env
use Daycry\JWT\JWT;

$jwt = JWT::for();   // pulls config('JWT')

// Encode
$token = $jwt->encode(['user_id' => 42, 'role' => 'admin'], 'user-42');

// Decode + validate (throws on failure)
$claims = $jwt->decode($token);                  // Plain
echo $claims->claims()->get('uid');              // "user-42"

// Symmetric helper — get the original payload back
$payload = $jwt->getPayload($token);             // ['user_id' => 42, 'role' => 'admin']

// Non-throwing alternative
$claims = $jwt->tryDecode($maybeBadToken);
if ($claims === null) {
    return $this->response->setStatusCode(401);
}

The library throws JWTConfigurationException if jwt.signer, jwt.issuer, jwt.audience, or jwt.identifier is empty. Defaults are intentionally null to fail loudly.

Configuration

After publishing, edit app/Config/JWT.php. All properties are inherited from Daycry\JWT\Config\JWT and overridable via .env.

HMAC (default)

jwt.algorithmType = "symmetric"
jwt.signer        = "<base64-secret-from-jwt:key>"
jwt.issuer        = "https://api.my-app.com"
jwt.audience      = "https://my-app.com"
jwt.identifier    = "my-app-v2"
jwt.expiresAt     = "+1 hour"
jwt.leeway        = "30"

RSA / ECDSA

php spark jwt:keypair --algorithm=rsa --bits=2048 --output=writable/keys
jwt.algorithmType = "asymmetric"
jwt.signingKey    = "/var/www/app/writable/keys/jwt-private.pem"
jwt.verifyingKey  = "/var/www/app/writable/keys/jwt-public.pem"
jwt.issuer        = "https://api.my-app.com"
jwt.audience      = "https://my-app.com"
jwt.identifier    = "my-app-v2"

In app/Config/JWT.php set the signer class:

public string $algorithm = \Lcobucci\JWT\Signer\Rsa\Sha256::class;   // RS256
// or \Lcobucci\JWT\Signer\Ecdsa\Sha256::class for ES256

See docs/configuration.md for the full reference.

Usage

Compact array payload (default)

$token = $jwt->encode(['user_id' => 1, 'role' => 'admin']);

$payload = $jwt->getPayload($token);  // ['user_id' => 1, 'role' => 'admin']

Split mode — claims at the top level

$jwt   = JWT::for()->withSplitData();
$token = $jwt->encode(['user_id' => 1, 'role' => 'admin']);
$claims = $jwt->decode($token);

echo $claims->claims()->get('role');  // "admin"

Custom payload claim name

$jwt = JWT::for()->withParamData('payload');
$jwt->getPayload($jwt->encode('hello'));  // "hello"

Clock skew tolerance (LooseValidAt)

$jwt = JWT::for()->withLeeway(30); // accept up to ±30s of skew

Error Handling

use Daycry\JWT\Exceptions\InvalidTokenException;
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;

try {
    $claims = $jwt->decode($token);
} catch (RequiredConstraintsViolated $e) {
    // Signature, issuer, audience, exp, etc.
    return $this->response->setStatusCode(401)->setJSON(['error' => $e->getMessage()]);
} catch (InvalidTokenException $e) {
    // Malformed or non-Plain token.
    return $this->response->setStatusCode(400)->setJSON(['error' => 'Bad token']);
}

For a non-throwing flow:

$claims = $jwt->tryDecode($token);
if ($claims === null) {
    return $this->response->setStatusCode(401);
}

Utility Methods

Method Returns Description
decode(string $token) Plain Validates and returns the parsed token. Throws on failure.
tryDecode(string $token) ?Plain Same as decode() but returns null on failure.
getPayload(string $token) mixed Validates + returns the original payload (auto-decoded for compact mode).
isValid(string $token) bool True iff tryDecode() succeeds.
isExpired(string $token) bool True for malformed tokens or tokens past exp.
getTimeToExpiry(string $token) ?int Seconds until exp, or null.
extractClaimsUnsafe(string $token) ?array Claims without validation. Logs a warning unless Config::$allowUnsafeExtraction = true.

CLI Commands

# Publish config to app/Config/JWT.php
php spark jwt:publish

# Generate an HMAC key (default 32 bytes) and write to .env
php spark jwt:key
php spark jwt:key 64 --show
php spark jwt:key --force

# Generate an asymmetric key pair
php spark jwt:keypair --algorithm=rsa   --bits=2048
php spark jwt:keypair --algorithm=ecdsa --curve=prime256v1 --output=writable/keys

Security Best Practices

  1. Use a strong key — at least 32 bytes (php spark jwt:key).
  2. Set short expiry times for API access tokens (+15 minutes).
  3. Enable all validation constraints in production.
  4. Never commit .env or any file containing jwt.signer.
  5. Rotate the key immediately if exposed — all outstanding tokens become invalid.

Testing

composer test
# or without coverage (faster)
vendor/bin/phpunit --no-coverage

Documentation

Full reference documentation is in the docs/ folder:

Document Description
Getting Started Installation and first token in minutes
Configuration Every property, its type, default, and .env key
Usage Complete API reference with examples
CLI Commands jwt:key and jwt:publish reference
Advanced Utility methods, middleware, multi-tenant patterns
Testing Test suite structure and writing new tests

Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/my-feature
  3. Commit your changes: git commit -m 'Add my feature'
  4. Push and open a Pull Request

License

MIT — see LICENSE.

Support