shahzadbarkati/role-based-jwt-auth
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.
tymon/jwt-auth with configurable TTL and blacklisting.composer require shahzadbarkati/role-based-jwt-auth
This auto-registers the service provider via Laravel's package discovery.
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.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
JWT_BLACKLIST_ENABLED=true
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).
php artisan jwt-auth:migrate-prompt
php artisan migrate
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);
Add the published HasRoles trait and JWT settings 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
use Tymon\JWTAuth\Contracts\JWTSubject; // import
class User extends Authenticatable implements JWTSubject
{
use HasRoles, Notifiable; // Use the trait
// ... (rest of your User model unchanged)
// Required for JWT: Return the ID claim
public function getJWTIdentifier()
{
return $this->getKey();
}
// Required for JWT: Return custom claims (e.g., add roles here if needed)
public function getJWTCustomClaims()
{
return [
'roles' => $this->roles->pluck('slug')->toArray(), // Optional: Include roles in token
];
}
}
This adds roles(), hasRole($role), assignRole($role), and removeRole($role) methods.
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');
});
The package merges JWT guards. Verify config/auth.php:
'guards' => [
....,
'api' => [
'driver' => 'jwt', // Added by package
'provider' => 'users',
],
],
Edit config/jwt-auth.php for customizations:
Routes are auto-loaded at /api/auth (configurable). View with php artisan route:list.
// Request
{
"email": "user@example.com",
"password": "password"
}
// Response
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
}
// Response
{
"id": 1,
"email": "user@example.com",
"roles": ["user"]
}
// Request
{
"email": "user@example.com"
}
// Email sent: "Your reset code is: 123456 (expires in 10 mins)"
// Request
{
"email": "user@example.com",
"code": "123456",
"password": "newpassword",
"password_confirmation": "newpassword"
}
// Response
{
"message": "Password reset"
}
Protect routes with role:slug middleware:
// In routes/api.php
Route::middleware(['auth:api', 'role:admin'])->get('/admin/dashboard', function () {
return 'Admin only';
});
$user = User::find(1);
$user->assignRole('admin'); // Or Role::create(['name' => 'Admin', 'slug' => 'admin']); $user->assignRole($role);
For custom logic:
use ShahzadBarkati\RoleBasedJwtAuth\Facades\JwtAuth;
$token = JwtAuth::login(['email' => 'user@example.com', 'password' => 'pass']);
JwtAuth::logout($token);
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
phpunit.xml: <env name="JWT_SECRET" value="base64:your_test_key"/>Example Test:
uses(RefreshDatabase::class)->in(__DIR__);
$response = $this->postJson('/api/auth/login', [
'email' => 'test@example.com',
'password' => 'password',
]);
// Assert: Success response with token
$response
->assertStatus(200)
->assertJsonStructure([
'token' => ['*'],
]);
// Verify token is a string
expect($response->json('token'))->toBeString();
composer test and php artisan cs:fix (Pest/PHPStan).MIT. See LICENSE for details.
How can I help you explore Laravel packages today?