mwy/laravel-auth-suite

Blade authentication package with two-factor authentication and device management for Laravel.

Maintainers

Package info

github.com/adindaofficial/laravel-auth-suite

Language:Blade

pkg:composer/mwy/laravel-auth-suite

Statistics

Installs: 9

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-05-28 18:03 UTC

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 users table with name, email, and password
  • 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
  • --force overwrites generated package files in the application.
  • --skip-app-files publishes 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/Auth for login, register, reset password, email verification, and two-factor challenge
  • resources/views/Auth/Profile for profile and password pages
  • resources/views/Auth/Settings for settings, security, and devices pages
  • resources/views/layouts/app.blade.php for the main layout
  • resources/views/layouts/partials/navbar.blade.php for the navbar and account dropdown
  • resources/views/layouts/partials/footer.blade.php for the footer

The default navbar account dropdown contains only Profile, Settings, and Logout.

Security Notes

  • Passwords are hashed with Laravel's Hash service.
  • 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-autoload and php artisan package:discover.
  • If generated files are not updated, run php artisan auth:install --force.
  • If two-factor methods are missing, confirm HasTwoFactorAuthentication is added to the User model.
  • If Email OTP is not delivered, confirm the application's MAIL_* SMTP values are valid and run php 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_THRESHOLD gradually, for example from 0.48 to 0.52.
  • If Face MFA is too permissive, lower AUTH_SUITE_FACE_MATCH_THRESHOLD, for example from 0.48 to 0.44.
  • If devices are not listed, set SESSION_DRIVER=database and run php artisan migrate.
  • If route changes are not visible, run php artisan optimize:clear.
  • If columns already exist, the migrations skip them using Schema::hasColumn and Schema::hasTable.
  • If routes conflict with your application, set AUTH_SUITE_ROUTE_PREFIX=auth.