kz370/jwt-auth

Secure JWT access token + refresh token authentication for Laravel

Installs: 8

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/kz370/jwt-auth

v1.0.0 2025-12-21 21:19 UTC

This package is auto-updated.

Last update: 2025-12-21 21:19:13 UTC


README

Latest Version on Packagist Total Downloads Software License PHP Version Laravel Version

A sophisticated, secure, and developer-friendly JWT authentication package for Laravel. Designed with a dual-token architecture (Access + Refresh tokens) and advanced security features like automatic rotation and replay attack detection.

🚀 Key Features

  • Dual-Token Architecture: Implements short-lived Access Tokens for security and long-lived Refresh Tokens for a seamless user experience.
  • Secure Token Management: All refresh tokens are hashed (SHA-256) before storage, ensuring data safety even in the event of a database compromise.
  • Automatic Token Rotation: Implements a "sliding session" approach where a new refresh token is issued on every use, immediately invalidating the previous one.
  • Advanced Replay Detection: Real-time monitoring of token families. If a previously used refresh token is re-submitted, the system detects a breach and revokes the entire token family.
  • Granular Device Control: Native support for tracking, listing, and revoking specific device sessions from anywhere in your application.
  • Zero-Config Integration: Drop-in replacement for standard Laravel guards (Sanctum/Passport).

📦 Installation

Install the package via Composer:

composer require kz370/jwt-auth

1. Run Migrations

The package requires specific tables to manage token families and device sessions.

php artisan migrate

2. Publish Configuration (Optional)

Customize the TTL (Time-To-Live), signing algorithm, and other settings:

php artisan vendor:publish --tag=jwt-auth-config

3. Generate JWT Secret

Generate a secure signing key for your tokens. This will be added to your .env file:

php artisan jwt:secret

👨‍💻 User Model Setup

To enable session management and token relationships on your User model, add the HasJwtAuth trait:

use Kz370\JwtAuth\Traits\HasJwtAuth;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use HasJwtAuth;
    
    // ...
}

This trait provides several helper methods:

  • $user->jwtTokens: Get all active sessions.
  • $user->currentJwtToken(): Get the session model for the current request.

🎭 Multi-Model & Multi-Guard Support

The package is not limited to the User model. You can use it with any Eloquent model (Admins, Customers, etc.) and even manage multiple guards simultaneously.

1. Custom Model

If you only use one model but it's not App\Models\User, update your config/jwt-auth.php:

'user_model' => App\Models\Admin::class,

2. Multiple Guards (e.g., User and Admin)

If you need separate authentication for different tables, define them in config/auth.php:

// config/auth.php
'guards' => [
    'jwt' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
    'admin-jwt' => [
        'driver' => 'jwt',
        'provider' => 'admins',
    ],
],

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\Models\User::class,
    ],
    'admins' => [
        'driver' => 'eloquent',
        'model' => App\Models\Admin::class,
    ],
],

Then protect your routes accordingly:

Route::middleware('auth:admin-jwt')->get('/admin/profile', ...);

How it works

The package uses a polymorphic relationship in the database. Instead of a simple user_id, the tokens table contains:

  • authenticatable_id: The ID of the record (e.g., 1).
  • authenticatable_type: The class name of the model (e.g., App\Models\Admin).

This design ensures that sessions are perfectly isolated, even if two different models share the same ID.

Note: Ensure every model used for authentication includes the HasJwtAuth trait.

⚙️ Configuration

Automatic Guard Registration

The package automatically registers a jwt authentication guard. To use it as your default for API routes, update your config/jwt-auth.php:

// config/jwt-auth.php
'override_default_guard' => true,

🛡 Middleware Usage

The package provides two middlewares out of the box to help you secure your routes.

1. jwt.auth

Protects routes that require a valid Access Token. It automatically validates the JWT and sets the authenticated user for the request.

// routes/api.php
Route::middleware('jwt.auth')->get('/user', function (Request $request) {
    return $request->user();
});

2. jwt.refresh

Ensures that the request contains a refresh_token. Useful for specific refresh or logout endpoints.

Route::middleware('jwt.refresh')->post('/refresh', [AuthController::class, 'refresh']);

⚡ Integration with Existing Auth

If you are migrating from Laravel Sanctum or Passport, you simply need to replace your token generation logic in your authentication controllers.

Find where you currently generate tokens (e.g., $user->createToken(...)) and replace it with the JwtAuth facade:

use Kz370\JwtAuth\Facades\JwtAuth;

public function login(Request $request) 
{
    // ... your validation logic ...
    $user = User::where('email', $request->email)->first();

    // Replace $user->createToken('...')->plainTextToken with:
    // Simplified: IP and User-Agent are detected automatically
    // You can just pass the device name as a string:
    $tokens = JwtAuth::login($user, 'iPhone 15 Pro');

    // $tokens content: ['access_token', 'refresh_token', 'expires_in', ...]
    return response()->json($tokens);
}

This ensures that users transitioning to this package correctly adopt the new dual-token system without leaving behind outdated logic.

🛠 Usage

Authentication (The Facade)

The JwtAuth facade is the primary entry point for all operations.

User Login (Credentials)

use Kz370\JwtAuth\Facades\JwtAuth;

public function login(Request $request)
{
    $credentials = $request->only('email', 'password');
    
    // Choose your preferred syntax:
    
    // 1. Fully Automatic (detects IP and UA, sets device to null)
    $tokens = JwtAuth::attempt($credentials);

    // 2. Simplified Device Name (detects IP and UA automatically)
    $tokens = JwtAuth::attempt($credentials, 'iPhone 15 Pro');

    // 3. Full Manual Control
    $tokens = JwtAuth::attempt($credentials, [
        'device_name' => 'MacBook Pro',
        'ip_address'  => '1.1.1.1'
    ]);

    if (!$tokens) {
        return response()->json(['error' => 'Unauthorized'], 401);
    }

    return response()->json($tokens);
}

Token Refresh

Exchange a refresh token for a brand new pair of tokens (rotates the family).

public function refresh(Request $request)
{
    $tokens = JwtAuth::refresh($request->refresh_token);

    if (!$tokens) {
        return response()->json(['error' => 'Invalid or expired token'], 401);
    }

    return response()->json($tokens);
}

Logout

Invalidates the current refresh token and session. Returns true on success, or false if the token is invalid/expired.

public function logout(Request $request)
{
    $revoked = JwtAuth::logout($request->refresh_token);
    
    if (!$revoked) {
        return response()->json(['message' => 'Invalid or already revoked token'], 401);
    }

    return response()->json(['message' => 'Logged out successfully']);
}

📱 Device & Session Management

Take full control of user sessions across multiple devices:

// List all active sessions for a user
$sessions = JwtAuth::getDevices($userId);

// Revoke a specific session
JwtAuth::revokeDevice($userId, $sessionId);

// Global Logout: Revoke all sessions for a user
JwtAuth::logoutAll($userId);

// Revoke all OTHER sessions (stay logged in on current device)
JwtAuth::logoutOthers($currentRefreshToken);

🔒 Security Design

Family IDs & Token Rotation

Every login starts a "Token Family". When you refresh, the old refresh token is revoked, and a new one is issued within the same family.

Replay Attack Protection

If a used refresh token is ever presented again (indicating it was stolen and replayed), the package detects this immediately and revokes every token in that family, forcing the legitimate user to re-authenticate and securing the account.

🖥 Console Commands

Command Description
php artisan jwt:secret Generates a 64-character secret key for JWT signing.
php artisan jwt:cleanup Removes expired and revoked tokens from the database.

Recommendation: Schedule the cleanup command to run daily:

// routes/console.php
use Illuminate\Support\Facades\Schedule;
Schedule::command('jwt:cleanup')->daily();

📄 License

The MIT License (MIT). Please see License File for more information.