Laravel 2026 Multi-Auth System (Admin + User) — Full Tutorial

Author

Kritim Yantra

Dec 20, 2025

Laravel 2026 Multi-Auth System (Admin + User) — Full Tutorial

Have you ever deployed your first PHP/Laravel project and thought:

  • “Why is my admin page showing to normal users?!”
  • “Why does login work locally but feels messy in production?”
  • “Why did I just lock myself out of my own admin panel?” 😭

I’ve been there. You build a nice Laravel app, push it live, and suddenly you realize authentication isn’t just “login/register”… it’s also who can access what.

A proper multi-auth setup (Admin + User) saves you from:

  • Security headaches (users shouldn’t even touch admin routes)
  • Messy code (no more if($user->is_admin) everywhere)
  • Deployment drama (clear separation = fewer “it works on my machine” moments)

Today we’ll build a clean Laravel 2026 multi-auth system with:

✅ Separate Admin and User login
✅ Separate guards (web and admin)
✅ Separate middleware (auth vs auth:admin)
✅ Admin & User profile pages
✅ Clean routes: /admin/... and /user/...


What we’re building (quick mental picture)

Here’s the flow:

Visitor
  ├── /register  -> creates User
  ├── /login     -> logs in User (guard: web)
  └── /admin/login -> logs in Admin (guard: admin)
  
User session -> can access /user/dashboard, /user/profile
Admin session -> can access /admin/dashboard, /admin/profile

Think of it like two different keys 🔑:

  • User key opens user doors
  • Admin key opens admin doors
    And middleware is the security guard checking which key you’re holding.

Prerequisites

  • Laravel (2026 era, works great with modern Laravel versions)
  • PHP 8.2+ (recommended)
  • MySQL (or any DB you like)

Step 1: Create a fresh Laravel project

composer create-project laravel/laravel multi-auth-2026
cd multi-auth-2026

Set your .env database:

DB_DATABASE=multi_auth
DB_USERNAME=root
DB_PASSWORD=

Run migrations:

php artisan migrate

At this point, you have a default users table.


Step 2: Install user auth scaffolding (fastest beginner-friendly way)

If you want ready-made User login/register pages, Laravel Breeze is a simple option:

composer require laravel/breeze --dev
php artisan breeze:install
php artisan migrate
npm install
npm run dev

Now you have:

  • /register
  • /login
  • /profile (depending on stack)
  • user session (guard: web)

Pro Tip: Breeze keeps things clean. It’s great for beginners because you’re not reinventing the wheel.


Step 3: Create an Admin model + migration (separate table)

Now we’ll create a completely separate admin system.

php artisan make:model Admin -m

Edit the generated migration in database/migrations/...create_admins_table.php:

public function up(): void
{
    Schema::create('admins', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->string('email')->unique();
        $table->timestamp('email_verified_at')->nullable();
        $table->string('password');
        $table->rememberToken();
        $table->timestamps();
    });
}

Run migration:

php artisan migrate

Now you have users and admins tables.


Step 4: Configure Guards (the heart of multi-auth)

Open config/auth.php.

4.1 Add an admin guard

Find 'guards' and update like this:

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'admin' => [
        'driver' => 'session',
        'provider' => 'admins',
    ],
],

4.2 Add an admin provider

Find 'providers':

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\Models\User::class,
    ],

    'admins' => [
        'driver' => 'eloquent',
        'model' => App\Models\Admin::class,
    ],
],

Now Laravel understands there are two types of accounts.


Step 5: Make Admin authentication (Login + Logout)

We’ll create a controller for admin auth:

php artisan make:controller Admin/AuthController

5.1 Create admin login routes

Open routes/web.php:

use App\Http\Controllers\Admin\AuthController as AdminAuthController;
use App\Http\Controllers\Admin\AdminController;

Route::prefix('admin')->name('admin.')->group(function () {

    Route::get('/login', [AdminAuthController::class, 'showLogin'])
        ->middleware('guest:admin')
        ->name('login');

    Route::post('/login', [AdminAuthController::class, 'login'])
        ->middleware('guest:admin')
        ->name('login.submit');

    Route::post('/logout', [AdminAuthController::class, 'logout'])
        ->middleware('auth:admin')
        ->name('logout');

    Route::get('/dashboard', [AdminController::class, 'dashboard'])
        ->middleware('auth:admin')
        ->name('dashboard');
});

5.2 Build the controller logic

Open app/Http/Controllers/Admin/AuthController.php:

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class AuthController extends Controller
{
    public function showLogin()
    {
        return view('admin.auth.login');
    }

    public function login(Request $request)
    {
        $request->validate([
            'email' => ['required', 'email'],
            'password' => ['required'],
        ]);

        if (Auth::guard('admin')->attempt(
            $request->only('email', 'password'),
            $request->boolean('remember')
        )) {
            $request->session()->regenerate();
            return redirect()->route('admin.dashboard');
        }

        return back()->withErrors([
            'email' => 'Invalid admin credentials.',
        ])->onlyInput('email');
    }

    public function logout(Request $request)
    {
        Auth::guard('admin')->logout();
        $request->session()->invalidate();
        $request->session()->regenerateToken();

        return redirect()->route('admin.login');
    }
}

Warning ️: Don’t use Auth::attempt() for admin. Always use Auth::guard('admin')->attempt() — otherwise Laravel logs you into the user guard and you’ll get super confusing behavior.


Step 6: Create Admin dashboard controller

php artisan make:controller Admin/AdminController

app/Http/Controllers/Admin/AdminController.php:

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;

class AdminController extends Controller
{
    public function dashboard()
    {
        return view('admin.dashboard');
    }
}

Step 7: Create Admin views (login + dashboard)

Create folders:

  • resources/views/admin/auth/
  • resources/views/admin/

7.1 Admin Login page

resources/views/admin/auth/login.blade.php:

<!doctype html>
<html>
<head>
    <title>Admin Login</title>
</head>
<body>
    <h2>Admin Login</h2>

    @if ($errors->any())
        <div style="color:red;">
            <ul>
                @foreach ($errors->all() as $error)
                    <li>{{ $error }}</li>
                @endforeach
            </ul>
        </div>
    @endif

    <form method="POST" action="{{ route('admin.login.submit') }}">
        @csrf

        <div>
            <label>Email</label>
            <input type="email" name="email" value="{{ old('email') }}" required>
        </div>

        <div>
            <label>Password</label>
            <input type="password" name="password" required>
        </div>

        <div>
            <label>
                <input type="checkbox" name="remember">
                Remember Me
            </label>
        </div>

        <button type="submit">Login</button>
    </form>
</body>
</html>

7.2 Admin Dashboard page

resources/views/admin/dashboard.blade.php:

<!doctype html>
<html>
<head>
    <title>Admin Dashboard</title>
</head>
<body>
    <h2>Welcome Admin 🎉</h2>

    <p>You are logged in as: <b>{{ auth('admin')->user()->email }}</b></p>

    <form method="POST" action="{{ route('admin.logout') }}">
        @csrf
        <button type="submit">Logout</button>
    </form>
</body>
</html>

Step 8: Add Admin Profile page (view + update)

Now let’s build /admin/profile similar to user profile, but for admins.

8.1 Create Profile Controller

php artisan make:controller Admin/AdminProfileController

Add routes (inside the admin group):

use App\Http\Controllers\Admin\AdminProfileController;

Route::get('/profile', [AdminProfileController::class, 'edit'])
    ->middleware('auth:admin')
    ->name('profile.edit');

Route::post('/profile', [AdminProfileController::class, 'update'])
    ->middleware('auth:admin')
    ->name('profile.update');

8.2 Controller logic

app/Http/Controllers/Admin/AdminProfileController.php:

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;

class AdminProfileController extends Controller
{
    public function edit()
    {
        $admin = auth('admin')->user();
        return view('admin.profile', compact('admin'));
    }

    public function update(Request $request)
    {
        $admin = auth('admin')->user();

        $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'password' => ['nullable', 'min:8', 'confirmed'],
        ]);

        $admin->name = $request->name;

        if ($request->filled('password')) {
            $admin->password = Hash::make($request->password);
        }

        $admin->save();

        return back()->with('success', 'Profile updated successfully!');
    }
}

8.3 Admin profile view

resources/views/admin/profile.blade.php:

<!doctype html>
<html>
<head>
    <title>Admin Profile</title>
</head>
<body>
    <h2>Admin Profile</h2>

    @if(session('success'))
        <p style="color:green;">{{ session('success') }}</p>
    @endif

    <form method="POST" action="{{ route('admin.profile.update') }}">
        @csrf

        <div>
            <label>Name</label>
            <input type="text" name="name" value="{{ old('name', $admin->name) }}" required>
        </div>

        <hr>

        <p><b>Change Password</b> (optional)</p>

        <div>
            <label>New Password</label>
            <input type="password" name="password">
        </div>

        <div>
            <label>Confirm New Password</label>
            <input type="password" name="password_confirmation">
        </div>

        <button type="submit">Save Changes</button>
    </form>

    <p><a href="{{ route('admin.dashboard') }}">Back to Dashboard</a></p>
</body>
</html>

Step 9: User dashboard + profile routes (clean separation)

If you used Breeze, you likely already have /dashboard and /profile.

But for a “middleware-based tutorial feel”, here’s the pattern I recommend:

Route::prefix('user')->name('user.')->middleware('auth')->group(function () {
    Route::get('/dashboard', function () {
        return view('user.dashboard');
    })->name('dashboard');

    Route::get('/profile', function () {
        return view('user.profile');
    })->name('profile');
});

Then your user dashboard view could use:

{{ auth()->user()->email }}

And admin views use:

{{ auth('admin')->user()->email }}

That tiny difference is everything.


Step 10: Create an Admin account (Seeder)

Because admins aren’t registering publicly (good!), we’ll seed one.

php artisan make:seeder AdminSeeder

database/seeders/AdminSeeder.php:

<?php

namespace Database\Seeders;

use App\Models\Admin;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;

class AdminSeeder extends Seeder
{
    public function run(): void
    {
        Admin::updateOrCreate(
            ['email' => 'admin@example.com'],
            [
                'name' => 'Super Admin',
                'password' => Hash::make('password123'),
            ]
        );
    }
}

Run it:

php artisan db:seed --class=AdminSeeder

Now go to:

  • http://127.0.0.1:8000/admin/login
  • Email: admin@example.com
  • Pass: password123

Warning : Change that password immediately on real projects. Also, never commit real admin credentials to GitHub.


Common mistakes (I learned this the hard way 😅)

  • Logging in admin using Auth::attempt() (wrong guard!)

  • Forgetting guest:admin on admin login route (causes redirect loops)

  • Mixing sessions and thinking “why did admin logout log out my user too?”

    • Answer: you used the wrong guard or reused routes incorrectly.

Pro Tip ✅: Always prefix routes (admin/..., user/...). It prevents confusion and keeps your app organized like a clean room.


Conclusion (quick recap + encouragement)

You just built a real Laravel multi-auth system with:

  • Separate tables (users, admins)
  • Separate guards (web, admin)
  • Middleware protection (auth, auth:admin)
  • Separate login & profile pages
  • Admin routes safely under /admin

Now your project feels like a real production app — cleaner, safer, and easier to maintain.

If you try just one thing after reading this:
Add the admin guard + auth:admin middleware and protect your admin routes. That single step prevents so many “oops” moments.


FAQ (Beginner-friendly)

1) Why do we need two guards? Can’t we just use one login?

You can use one login with roles, but two guards make separation cleaner. It’s like having two doors with two locks instead of one door and hoping everyone behaves.

2) Why do I get redirected back to login again and again (loop)?

This usually happens when:

  • You used auth instead of auth:admin (or vice versa)
  • You forgot guest:admin on the admin login page
  • Your guard config in config/auth.php is incomplete

3) Can I allow admins to manage users without mixing sessions?

Yes. Admin can manage users via models and controllers normally. The key is: admin authentication stays separate, but admin can still read/write User records.


Your turn 👇

What’s the biggest struggle you’ve faced while setting up authentication in Laravel — redirects, middleware confusion, admin access, or something else? Share it in the comments (and if you want, I’ll suggest the cleanest fix).

Tags

Comments

No comments yet. Be the first to comment!

Please log in to post a comment:

Sign in with Google

Related Posts