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
Requires
- php: ^8.2
- laravel/framework: ^12.0
- tymon/jwt-auth: ^2.0
Suggests
- laravel/sanctum: For additional session auth if needed
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.
# | Method | Endpoint | Description | Auth Required |
---|---|---|---|---|
1. | POST | /api/auth/login | Login with email/password | No |
2. | POST | /api/auth/logout | Logout (invalidate token) | Yes |
3. | POST | /api/auth/refresh | Refresh token | Yes |
4. | GET | /api/auth/profile | Get user profile | Yes |
5. | POST | /api/auth/forgot-password | Send reset code to email | No |
6. | POST | /api/auth/reset-password | Reset with code/password | No |
7. | PUT | /api/auth/password | Update password | Yes |
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
andphp artisan cs:fix
(Pest/PHPStan).
License
MIT. See LICENSE for details.