mwy / laravel-auth-suite
Blade authentication package with two-factor authentication and device management for Laravel.
Package info
github.com/adindaofficial/laravel-auth-suite
Language:Blade
pkg:composer/mwy/laravel-auth-suite
Requires
- php: ^8.2
- bacon/bacon-qr-code: ^2.0
- illuminate/auth: ^10.0|^11.0|^12.0
- illuminate/database: ^10.0|^11.0|^12.0
- illuminate/routing: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
- illuminate/validation: ^10.0|^11.0|^12.0
- illuminate/view: ^10.0|^11.0|^12.0
- laravel/socialite: ^5.0
- pragmarx/google2fa-laravel: ^2.2
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0
This package is auto-updated.
Last update: 2026-05-29 18:23:24 UTC
README
Reusable Laravel authentication package using plain Blade views, Tailwind CSS CDN, multi-factor authentication, account settings, and device management.
This package is designed to give Laravel projects a ready-to-use authentication module without Fortify, Jetstream, Livewire, Inertia, or Laravel UI.
Features
- Login, register, logout, forgot password, reset password, and optional email verification
- Plain Blade views with Tailwind CSS CDN
- Clean application layout, navbar, footer, dashboard, profile, settings, and security pages
- Multi-factor authentication with three selectable methods: Authenticator app, Email OTP, or Face verification
- Authenticator app QR code and TOTP verification
- Email OTP delivery through Laravel mail/SMTP configuration
- Face verification through an in-app camera scan
- OTP challenge, biometric challenge, recovery codes, and recovery code regeneration
- Encrypted authenticator secret, encrypted recovery codes, and encrypted face descriptors
- Device/session management with the ability to disconnect other devices
- Login rate limiting and session regeneration
- Configurable route prefix, redirects, registration, password reset, and email verification
- Install command that copies routes, controllers, middleware, services, traits, views, config, and migrations into the Laravel application
Requirements
- PHP 8.2+
- Laravel 10, 11, or 12
- A standard
userstable withname,email, andpassword - HTTPS for Face MFA in production because browser camera access requires a secure origin
Installation
Install from Packagist:
composer require mwy/laravel-auth-suite php artisan auth:install --force php artisan migrate php artisan optimize:clear
The default installer copies application-ready files into your Laravel app and appends a readable route block into routes/web.php.
Update Existing Installation
When updating the package in an existing Laravel project, use:
composer update mwy/laravel-auth-suite -W php artisan auth:install --force php artisan migrate php artisan optimize:clear
Installation From GitHub
If the repository is not installed from Packagist, add a VCS repository to the consuming project's composer.json:
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/mwy/laravel-auth-suite"
}
]
}
Then run:
composer require mwy/laravel-auth-suite php artisan auth:install --force php artisan migrate
Install Command
Primary command:
php artisan auth:install
Useful options:
php artisan auth:install --force php artisan auth:install --skip-app-files
--forceoverwrites generated package files in the application.--skip-app-filespublishes config and migrations only, without copying controllers, middleware, services, traits, views, or the route block.
The package also auto-installs once during Laravel package discovery. To disable auto-install before requiring the package, set:
AUTH_SUITE_AUTO_INSTALL=false
Generated Files
Default install copies these files into the Laravel application:
app/Http/Controllers/Auth
app/Http/Middleware/Auth
app/Services/Auth
app/Models/Traits
resources/views/Auth
resources/views/layouts
resources/views/layouts/partials
config/auth-suite.php
database/migrations
routes/web.php
After copying the route block to routes/web.php, the installer sets load_routes to false in config/auth-suite.php to avoid duplicate vendor and application routes.
User Model
Add the two-factor trait to App\Models\User.
If you use the copied application trait after running the default installer:
use App\Models\Traits\HasTwoFactorAuthentication; class User extends Authenticatable { use HasTwoFactorAuthentication; }
If you use package-managed vendor files instead:
use Mwy\LaravelAuthSuite\Traits\HasTwoFactorAuthentication; class User extends Authenticatable { use HasTwoFactorAuthentication; }
The package uses forceFill for package-managed fields, so it works with guarded or fillable model rules.
Device Management
Device management reads Laravel's sessions table. Use database sessions for the complete feature:
SESSION_DRIVER=database
Then run:
php artisan migrate
The package includes a guarded sessions migration. If the application already has a sessions table, the migration skips creation.
Users can open /settings/devices to:
- Review active sessions
- See the current device
- Disconnect one other device
- Disconnect all other devices after current-password confirmation
Two-factor Methods
Users can choose one MFA method from /settings/security.
Authenticator App
The authenticator method shows a QR code and verifies time-based OTP codes from an authenticator application. Recovery codes are generated after successful setup.
Email OTP
The email OTP method sends a one-time code to the user's email address using Laravel's mailer. Configure SMTP in the consuming Laravel application's .env:
MAIL_MAILER=smtp MAIL_HOST=smtp.example.com MAIL_PORT=587 MAIL_USERNAME=your-smtp-username MAIL_PASSWORD=your-smtp-password MAIL_ENCRYPTION=tls MAIL_FROM_ADDRESS=no-reply@example.com MAIL_FROM_NAME="${APP_NAME}"
Email OTP settings can be adjusted from config/auth-suite.php:
AUTH_SUITE_EMAIL_OTP_LENGTH=6 AUTH_SUITE_EMAIL_OTP_EXPIRES_MINUTES=10 AUTH_SUITE_EMAIL_OTP_RESEND_COOLDOWN_SECONDS=60 AUTH_SUITE_EMAIL_OTP_SUBJECT="Your verification code"
Face Verification
Face verification uses the browser camera and face recognition in the application page. It does not use third-party login, password manager prompts, browser passkeys, Face ID, or Windows Hello prompts.
The package does not store face photos. During setup, users open a dedicated scan page, the browser reads the face, and sends a numeric face descriptor to Laravel. The descriptor is stored encrypted and used later to compare the login scan.
Face MFA settings can be adjusted from config/auth-suite.php:
AUTH_SUITE_FACE_MFA_ENABLED=true AUTH_SUITE_FACE_MATCH_THRESHOLD=0.48 AUTH_SUITE_FACE_SAMPLE_COUNT=3 AUTH_SUITE_FACE_API_SCRIPT_URL=https://cdn.jsdelivr.net/npm/@vladmandic/face-api/dist/face-api.js AUTH_SUITE_FACE_API_MODEL_URL=https://cdn.jsdelivr.net/gh/vladmandic/face-api/model AUTH_SUITE_FACE_DETECTOR_INPUT_SIZE=224 AUTH_SUITE_FACE_DETECTOR_SCORE_THRESHOLD=0.55
Use HTTPS in production. Browser camera access is allowed on HTTPS and usually on local development origins such as localhost.
Configuration
Publish config manually if needed:
php artisan vendor:publish --tag=auth-suite-config
Main options in config/auth-suite.php:
return [ 'auto_install' => env('AUTH_SUITE_AUTO_INSTALL', true), 'load_routes' => env('AUTH_SUITE_LOAD_ROUTES', true), 'route_prefix' => env('AUTH_SUITE_ROUTE_PREFIX', ''), 'middleware' => ['web'], 'two_factor_enabled' => env('AUTH_SUITE_TWO_FACTOR_ENABLED', true), 'two_factor_default_method' => env('AUTH_SUITE_TWO_FACTOR_DEFAULT_METHOD', 'authenticator'), 'recovery_codes_count' => env('AUTH_SUITE_RECOVERY_CODES_COUNT', 8), 'app_name_for_qr' => env('AUTH_SUITE_QR_APP_NAME', env('APP_NAME', 'Laravel')), 'email_otp_length' => env('AUTH_SUITE_EMAIL_OTP_LENGTH', 6), 'email_otp_expires_minutes' => env('AUTH_SUITE_EMAIL_OTP_EXPIRES_MINUTES', 10), 'email_otp_resend_cooldown_seconds' => env('AUTH_SUITE_EMAIL_OTP_RESEND_COOLDOWN_SECONDS', 60), 'email_otp_subject' => env('AUTH_SUITE_EMAIL_OTP_SUBJECT', 'Your verification code'), 'face_mfa_enabled' => env('AUTH_SUITE_FACE_MFA_ENABLED', true), 'face_match_threshold' => env('AUTH_SUITE_FACE_MATCH_THRESHOLD', 0.48), 'face_sample_count' => env('AUTH_SUITE_FACE_SAMPLE_COUNT', 3), 'face_api_script_url' => env('AUTH_SUITE_FACE_API_SCRIPT_URL', 'https://cdn.jsdelivr.net/npm/@vladmandic/face-api/dist/face-api.js'), 'face_api_model_url' => env('AUTH_SUITE_FACE_API_MODEL_URL', 'https://cdn.jsdelivr.net/gh/vladmandic/face-api/model'), 'face_detector_input_size' => env('AUTH_SUITE_FACE_DETECTOR_INPUT_SIZE', 224), 'face_detector_score_threshold' => env('AUTH_SUITE_FACE_DETECTOR_SCORE_THRESHOLD', 0.55), 'redirect_after_login' => env('AUTH_SUITE_REDIRECT_AFTER_LOGIN', '/dashboard'), 'redirect_after_logout' => env('AUTH_SUITE_REDIRECT_AFTER_LOGOUT', '/login'), 'enable_registration' => env('AUTH_SUITE_ENABLE_REGISTRATION', true), 'enable_password_reset' => env('AUTH_SUITE_ENABLE_PASSWORD_RESET', true), 'enable_email_verification' => env('AUTH_SUITE_ENABLE_EMAIL_VERIFICATION', true), 'tailwind_cdn_enabled' => env('AUTH_SUITE_TAILWIND_CDN_ENABLED', true), ];
Example .env:
SESSION_DRIVER=database AUTH_SUITE_ROUTE_PREFIX= AUTH_SUITE_REDIRECT_AFTER_LOGIN=/dashboard AUTH_SUITE_REDIRECT_AFTER_LOGOUT=/login AUTH_SUITE_TWO_FACTOR_ENABLED=true AUTH_SUITE_TWO_FACTOR_DEFAULT_METHOD=authenticator AUTH_SUITE_RECOVERY_CODES_COUNT=8 AUTH_SUITE_EMAIL_OTP_LENGTH=6 AUTH_SUITE_EMAIL_OTP_EXPIRES_MINUTES=10 AUTH_SUITE_EMAIL_OTP_RESEND_COOLDOWN_SECONDS=60 AUTH_SUITE_EMAIL_OTP_SUBJECT="Your verification code" AUTH_SUITE_FACE_MFA_ENABLED=true AUTH_SUITE_FACE_MATCH_THRESHOLD=0.48 AUTH_SUITE_FACE_SAMPLE_COUNT=3 AUTH_SUITE_FACE_API_SCRIPT_URL=https://cdn.jsdelivr.net/npm/@vladmandic/face-api/dist/face-api.js AUTH_SUITE_FACE_API_MODEL_URL=https://cdn.jsdelivr.net/gh/vladmandic/face-api/model AUTH_SUITE_FACE_DETECTOR_INPUT_SIZE=224 AUTH_SUITE_FACE_DETECTOR_SCORE_THRESHOLD=0.55 AUTH_SUITE_ENABLE_REGISTRATION=true AUTH_SUITE_ENABLE_PASSWORD_RESET=true AUTH_SUITE_ENABLE_EMAIL_VERIFICATION=true AUTH_SUITE_TAILWIND_CDN_ENABLED=true
Routes
The package registers these routes under route_prefix:
| Method | URI | Name |
|---|---|---|
| GET | / |
home |
| GET | /login |
login |
| POST | /login |
login.store |
| POST | /logout |
logout |
| GET | /dashboard |
dashboard |
| GET | /register |
register |
| POST | /register |
register.store |
| GET | /forgot-password |
password.request |
| POST | /forgot-password |
password.email |
| GET | /reset-password/{token} |
password.reset |
| POST | /reset-password |
password.store |
| GET | /two-factor-challenge |
two-factor.challenge |
| POST | /two-factor-challenge |
two-factor.challenge.store |
| POST | /two-factor-challenge/resend |
two-factor.challenge.resend |
| GET | /two-factor-challenge/face/options |
two-factor.challenge.face.options |
| POST | /two-factor-challenge/face |
two-factor.challenge.face.verify |
| GET | /verify-email |
verification.notice |
| GET | /verify-email/{id}/{hash} |
verification.verify |
| POST | /email/verification-notification |
verification.send |
| GET | /profile |
profile.edit |
| PATCH | /profile |
profile.update |
| GET | /profile/password |
profile.password |
| PUT | /profile/password |
profile.password.update |
| GET | /settings |
settings.index |
| GET | /settings/security |
settings.security |
| POST | /settings/security/two-factor |
two-factor.enable |
| GET | /settings/security/face |
two-factor.face.scan |
| GET | /settings/security/face/options |
two-factor.face.options |
| POST | /settings/security/face |
two-factor.face.enable |
| DELETE | /settings/security/two-factor |
two-factor.disable |
| POST | /settings/security/recovery-codes |
two-factor.recovery-codes.regenerate |
| GET | /settings/devices |
settings.devices |
| DELETE | /settings/devices/{session} |
settings.devices.destroy |
| DELETE | /settings/devices |
settings.devices.destroy-other |
Registration, password reset, and email verification routes can be disabled from config.
Two-factor Flow
After a valid email and password login, users with two-factor enabled are not fully logged in. The package stores a temporary pending two-factor session and redirects to /two-factor-challenge.
If the user selected Authenticator app, the challenge accepts an authenticator OTP or an unused recovery code. If the user selected Email OTP, the package sends a code through SMTP and accepts that OTP or an unused recovery code. If the user selected Face verification, the challenge opens a focused scan screen, reads multiple face samples, compares them with the encrypted descriptor stored during setup, shows a success state, and then redirects.
A valid challenge response completes login, regenerates the session, clears the pending two-factor session, and redirects to the configured dashboard.
Recovery codes are single use. When a recovery code is accepted, it is removed from the encrypted recovery code list.
Recovery code entry is hidden behind an explicit option on every MFA challenge screen, so the primary verification method stays focused. Newly generated recovery codes are also collapsed by default and must be opened before viewing.
Protecting App Routes
Use Laravel's auth middleware for protected routes. To require two-factor verification for your own routes, add the package middleware alias:
Route::middleware(['auth', 'auth-suite.two_factor'])->group(function () { Route::get('/dashboard', DashboardController::class); });
Publishable Assets
Publish config:
php artisan vendor:publish --tag=auth-suite-config
Publish views:
php artisan vendor:publish --tag=auth-suite-views
Publish migrations:
php artisan vendor:publish --tag=auth-suite-migrations
Customizing Views
The installer places editable views in:
resources/views/Auth
resources/views/layouts
resources/views/layouts/partials
Important view groups:
resources/views/Authfor login, register, reset password, email verification, and two-factor challengeresources/views/Auth/Profilefor profile and password pagesresources/views/Auth/Settingsfor settings, security, and devices pagesresources/views/layouts/app.blade.phpfor the main layoutresources/views/layouts/partials/navbar.blade.phpfor the navbar and account dropdownresources/views/layouts/partials/footer.blade.phpfor the footer
The default navbar account dropdown contains only Profile, Settings, and Logout.
Security Notes
- Passwords are hashed with Laravel's
Hashservice. - Login attempts are rate limited by email and IP address.
- Session IDs are regenerated after successful login.
- Sessions are invalidated on logout.
- Users are not fully logged in before passing the two-factor challenge when two-factor is enabled.
- Email OTP codes are stored as hashes in the pending session and expire automatically.
- Email OTP resend is rate limited.
- Face MFA stores encrypted numeric face descriptors, not face photos.
- Face MFA depends on browser camera access, so production should use HTTPS.
- Face recognition can be affected by lighting, camera quality, pose, and similar-looking faces. Keep recovery codes available for account recovery.
- Device disconnect only removes sessions owned by the authenticated user.
- Disconnecting all other devices requires current-password confirmation.
- Two-factor secrets and recovery codes are encrypted with Laravel
Crypt. - Recovery codes are single use.
Troubleshooting
- If the install command is missing, run
composer dump-autoloadandphp artisan package:discover. - If generated files are not updated, run
php artisan auth:install --force. - If two-factor methods are missing, confirm
HasTwoFactorAuthenticationis added to the User model. - If Email OTP is not delivered, confirm the application's
MAIL_*SMTP values are valid and runphp artisan optimize:clear. - If Face MFA cannot open the camera, confirm the browser has camera permission and the application is running on HTTPS or localhost.
- If Face MFA often rejects valid users, increase
AUTH_SUITE_FACE_MATCH_THRESHOLDgradually, for example from0.48to0.52. - If Face MFA is too permissive, lower
AUTH_SUITE_FACE_MATCH_THRESHOLD, for example from0.48to0.44. - If devices are not listed, set
SESSION_DRIVER=databaseand runphp artisan migrate. - If route changes are not visible, run
php artisan optimize:clear. - If columns already exist, the migrations skip them using
Schema::hasColumnandSchema::hasTable. - If routes conflict with your application, set
AUTH_SUITE_ROUTE_PREFIX=auth.