shahzadbarkati/role-based-jwt-auth

Role based JWT authentication for Laravel 12 with forgot and reset password

Installs: 8

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

Type:laravel-package

pkg:composer/shahzadbarkati/role-based-jwt-auth

v1.1.6 2025-10-10 16:51 UTC

This package is auto-updated.

Last update: 2025-10-10 16:54:30 UTC


README

A lightweight, role-based JWT authentication package for Laravel 12. Built with SOLID principles, it provides secure API endpoints for login, logout, token refresh, profile management, password reset (with email codes), and password updates. Tokens are invalidated on new logins/refreshes for enhanced security.

Features

  • JWT Authentication: Powered by tymon/jwt-auth with configurable TTL and blacklisting.
  • Role-Based Access: Middleware for roles like admin, user. Supports many-to-many relationships.
  • Token Management: Auto-invalidate previous tokens (configurable max per user).
  • Password Handling: Forgot/reset with 6-digit codes (10-min expiration, customizable) via email.
  • Endpoints: Login, logout, refresh, profile, forgot-password, reset-password, update-password.
  • Easy Integration: Uses existing users table; optional migrations for roles/tokens.
  • Configurable: Full config for TTLs, guards, emails, routes, etc.

Requirements

  • PHP 8.2+
  • Laravel 12.0+
  • Database (MySQL/PostgreSQL/SQLite) for tokens/resets/roles.
  • Mail configuration for password resets (e.g., SMTP).

Installation

1. Require the Package

composer require shahzadbarkati/role-based-jwt-auth

This auto-registers the service provider via Laravel's package discovery.

2. Publish Assets

Publish the config, migrations, views, and trait:

php artisan vendor:publish --provider="ShahzadBarkati\RoleBasedJwtAuth\Providers\JwtAuthServiceProvider" --tag="jwt-auth"

This creates:

  • config/jwt-auth.php: Core configuration.
  • database/migrations/: Migrations for roles, pivot, and JWT tokens.
  • resources/views/vendor/jwt-auth/emails/password-reset.blade.php: Email template.
  • app/Traits/HasRoles.php: Trait for your User model.

3. Generate JWT Secret

Run the command to auto-generate and add JWT_SECRET (and suggested TTLs) to your .env:

php artisan jwt:secret

Example output:

textJWT_SECRET=base64:<your_generated_key>
Also set JWT_TTL=60
JWT_REFRESH_TTL=20160
PASSWORD_RESET_TTL=10

4. Run Migrations

The package prompts for migrations on first boot (in console). If skipped, run manually: Check if Migrations Are Needed:

If you have an existing users table: ✅ (package uses it).
For roles/tokens: New tables (roles, role_user, jwt_tokens).

Interactive Prompt (Recommended):

php artisan jwt-auth:migrate-prompt
  • This asks: "Run migrations for roles and tokens? (Y/N)"
  • Y: Automatically runs php artisan migrate (adds tables without overwriting existing data).
  • N: Skip; proceed to manual run below.

Manual Run (Alternative):

php artisan migrate

Optional: Add Indexes (for performance):

Run these after migration:

-- In your DB tool
ALTER TABLE jwt_tokens ADD INDEX idx_user_id (user_id);
ALTER TABLE jwt_tokens ADD INDEX idx_jti (jti);
ALTER TABLE jwt_tokens ADD INDEX idx_expires_at (expires_at);
ALTER TABLE password_resets ADD INDEX idx_email (email);

5. Configure Your User Model

Add the published HasRoles trait to app/Models/User.php:

<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use ShahzadBarkati\RoleBasedJwtAuth\Traits\HasRoles;  // Import the trait

class User extends Authenticatable
{
    use HasRoles, Notifiable;  // Use the trait

    // ... (rest of your User model unchanged)
}

This adds roles(), hasRole($role), assignRole($role), and removeRole($role) methods.

6. Configure Mail (for Password Resets)

Update .env for your mail driver (e.g., SMTP):

MAIL_MAILER=smtp
MAIL_HOST=smtp.example.com
MAIL_PORT=587
MAIL_USERNAME=your-email@example.com
MAIL_PASSWORD=your-password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=hello@example.com
MAIL_FROM_NAME="${APP_NAME}"

Test: php artisan tinker then

Mail::raw('Test', function ($message) { 
    $message->to('test@example.com'); 
});

7. Update Auth Config (Optional)

The package merges JWT guards. Verify config/auth.php:

'guards' => [
    'api' => [
        'driver' => 'jwt',  // Added by package
        'provider' => 'users',
    ],
],

Configuration

Edit config/jwt-auth.php for customizations:

  • JWT: TTL (60min default), refresh TTL (2 weeks), algo (HS256).
  • Guards: API guard settings.
  • Roles: Table names, default role, available roles array.
  • Password Reset: Code length (6), TTL (10min), email template/from.
  • Routes: Prefix (api/auth), middleware (api).
  • Max Tokens: Per user (1 default) for auto-invalidation.

Example overrides in .env:

JWT_TTL=30
PASSWORD_RESET_TTL=15
JWT_MAX_TOKENS_PER_USER=2

Usage

Routes

Routes are auto-loaded at /api/auth (configurable). View with php artisan route:list.

#MethodEndpointDescriptionAuth Required
1.POST/api/auth/loginLogin with email/passwordNo
2.POST/api/auth/logoutLogout (invalidate token)Yes
3.POST/api/auth/refreshRefresh tokenYes
4.GET/api/auth/profileGet user profileYes
5.POST/api/auth/forgot-passwordSend reset code to emailNo
6.POST/api/auth/reset-passwordReset with code/passwordNo
7.PUT/api/auth/passwordUpdate passwordYes

Request/Response Examples:

  • Login
// Request
{
  "email": "user@example.com",
  "password": "password"
}
// Response
{
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
}
  • Profile
// Response
{
  "id": 1,
  "email": "user@example.com",
  "roles": ["user"]
}
  • Forgot Password
// Request
{
  "email": "user@example.com"
}
// Email sent: "Your reset code is: 123456 (expires in 10 mins)"
  • Reset Password
// Request
{
  "email": "user@example.com",
  "code": "123456",
  "password": "newpassword",
  "password_confirmation": "newpassword"
}
// Response
{
  "message": "Password reset"
}

Role-Based Routes

Protect routes with role:slug middleware:

// In routes/api.php
Route::middleware(['auth:api', 'role:admin'])->get('/admin/dashboard', function () {
    return 'Admin only';
});

Assign Roles (e.g., in Seeder/Controller):

$user = User::find(1);
$user->assignRole('admin');  // Or Role::create(['name' => 'Admin', 'slug' => 'admin']); $user->assignRole($role);

Facade Usage

For custom logic:

use ShahzadBarkati\RoleBasedJwtAuth\Facades\JwtAuth;

$token = JwtAuth::login(['email' => 'user@example.com', 'password' => 'pass']);
JwtAuth::logout($token);

Seeding Roles (Optional)

Create a seeder: php artisan make:seeder RoleSeeder

<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use ShahzadBarkati\RoleBasedJwtAuth\Models\Role;

class RoleSeeder extends Seeder
{
    public function run(): void
    {
        $roles = ['admin', 'user', 'moderator'];
        foreach ($roles as $role) {
            Role::firstOrCreate(['slug' => $role], ['name' => ucfirst($role)]);
        }
    }
}

Run: php artisan db:seed --class=RoleSeeder

Testing

  • Add to phpunit.xml: <env name="JWT_SECRET" value="base64:your_test_key"/>
  • Create tests for endpoints (e.g., using actingAs with JWT).
  • Run: php artisan test

Example Test:

public function test_login_returns_token()
{
    $user = User::factory()->create(['password' => Hash::make('password')]);
    $response = $this->postJson('/api/auth/login', [
        'email' => $user->email,
        'password' => 'password',
    ]);
    $response->assertStatus(200)->assertJsonStructure(['token']);
}

Security Notes

  • Always validate inputs (built-in).
  • Use HTTPS for tokens.
  • Rotate secrets in production.
  • For high load, queue emails and use Redis for blacklists.

Contributing

  • Fork, branch, PR with tests.
  • Run composer test and php artisan cs:fix (Pest/PHPStan).

License

MIT. See LICENSE for details.