hesto/multi-auth
Hesto MultiAuth adds support for multiple authentication systems in Laravel, letting you configure separate guards, models, and tables for users, admins, or other account types, with login, registration, password reset, middleware, and Blade helpers for each.
Before you install: this package targets Laravel 5.3–5.7 only. Check your composer.json framework constraint first — on 5.8+ you'll hit a dependency conflict, and on 6+ you should use native guards plus a scaffolding tool instead. Its real value is one-shot code generation (models, migrations, controllers, routes) per user type; it is not a runtime auth engine.
Steps:
composer require hesto/multi-authHesto\MultiAuth\MultiAuthServiceProvider::class to config/app.php. On 5.5+ auto-discovery handles it.php artisan list multi-auth. The provider registers scaffolding commands (an install/setup command and a per-guard multi-auth:auth-style generator). Read vendor/hesto/multi-auth/src/Console/ if the README and your installed version disagree.php artisan multi-auth:install # publish config/stubs, wire the provider
php artisan multi-auth:auth admin # generate App\Admin, migration, AdminAuth controllers, routes
config/auth.php):'guards' => [
'web' => ['driver' => 'session', 'provider' => 'users'],
'admin' => ['driver' => 'session', 'provider' => 'admins'],
],
'providers' => [
'admins' => ['driver' => 'eloquent', 'model' => App\Admin::class],
],
'passwords' => [
'admins' => ['provider' => 'admins', 'table' => 'password_resets', 'expire' => 60],
],
php artisan migrate, then browse /admin/login. First use case: a customer-facing users guard plus a back-office admin guard with completely separate tables, models, login screens, and dashboards.One guard = one model = one table. Never point two providers at the same model. Each generated model extends Illuminate\Foundation\Auth\User (or Authenticatable with Notifiable, CanResetPassword) and needs remember_token, and on 5.7 email_verified_at if you want verification.
Route segmentation. Keep generated auth routes for each guard under their own prefix, and protect the app area with the guard-specific middleware:
Route::prefix('admin')->name('admin.')->group(function () {
// generated admin login/register/reset routes land here
Route::middleware('auth:admin')->group(function () {
Route::get('/home', 'AdminHomeController@index')->name('home');
});
});
Controllers. Inside each generated auth controller, set the guard explicitly rather than relying on the default:
protected $guard = 'admin';
protected $redirectTo = '/admin/home';
// LoginController@login
if (Auth::guard('admin')->attempt($request->only('email','password'), $request->filled('remember'))) {
return redirect()->intended($this->redirectTo);
}
Also set $this->middleware('guest:admin') in the constructor — this is the single most common thing the scaffolding gets subtly wrong for you.
Reading the user. Use auth('admin')->user(), Auth::guard('admin')->id(), and in Blade @auth('admin') … @endauth / @guest('admin'). Avoid the bare auth() helper inside shared views.
Coexisting sessions. Session storage is guard-scoped, so an admin can be logged into web and admin simultaneously. Log out per guard: Auth::guard('admin')->logout().
Password resets per guard. Each guard needs its own entry under passwords in config/auth.php and its own broker usage in the generated ForgotPasswordController/ResetPasswordController. If two guards share the password_resets table, key tokens by email and guard — or use separate tables.
Testing. $this->actingAs($admin, 'admin') (or be($admin, 'admin')) — always pass the guard name, or your assertions will silently run as the web user and produce false passes.
make:auth and shifted scaffolding; this package's generated code is frozen at 5.7 idioms. Treat it as a generator you run once and then delete from require-dev, not a long-term dependency.app/, routes/web.php, and database/migrations/. A second run can clobber hand-edited controllers. Diff carefully, or copy the package's stubs into your own namespace and edit those.auth:admin in routes, guest:admin in constructors, 'admin' in config, and admin in Blade must match exactly. Symptom: infinite redirect between /login and /admin/home.RedirectIfAuthenticated is the classic trap. The stock middleware checks only the default guard, so a logged-in admin hitting /admin/login gets bounced back to a page that redirects them to login again. Extend handle() to loop over config('auth.guards') and redirect to the matching home route.Handler::unauthenticated() needs branching. Default behaviour redirects to route('login') (the web login). Add per-guard logic keyed off $request->path() or a guard marker so admins land on /admin/login.$request->session()->invalidate() on single-guard logout — it wipes all guards. Use Auth::guard('admin')->logout() plus $request->session()->regenerateToken() if CSRF rotation matters.users and admins with a shared password_resets table means one guard's token can invalidate the other's. Separate tables or a guard column is the fix.ThrottlesLogins trait in the generated controller.php artisan route:list --middleware=auth:admin, php artisan route:list --path=admin, dd(config('auth.guards')), and Auth::guard('admin')->check() in tinker. If a provider silently falls back to web, you'll usually see the wrong model hydrated — dump get_class(auth()->user()).file, database, or redis session drivers; the cookie driver cannot persist per-guard state reliably.active/role columns, custom attempt() credential arrays, or event listeners on Illuminate\Auth\Events\Login freely. If you need API tokens, don't stretch this package: add Passport (5.3+) or Sanctum (newer) alongside it and give the API its own token/passport guard.How can I help you explore Laravel packages today?