agillapro's avatar

Multi Auth with different user tables

I'm building a paid membership site with Authentication that needs 2 different user types, a Member for our customers who have paid for access and an Admin for the company employees who manage the site (such as help desk agents, sales etc).

We're already using Spatie permissions to give fine grained access to various functions within the Admin area. I understand I could possibly build this solution just using roles and permissions.

But what if I needed to have the admin users in the users table and the User model, with a separate table and Authenticable model for Members? If I wanted to have a login page under /admin/login whose controller checked the users table, and a completely different login page under /login that worked off the members table, would this be possible?

I'm having a hard time wrapping my head around this. I see posts talking about multiple guards and I'm not sure how that plays into this either. How would I approach this requirement?

0 likes
7 replies
LaryAI's avatar
Level 58

To implement multi-authentication with different user tables in a Laravel application, you can use Laravel's authentication guards. This allows you to define separate authentication mechanisms for different user types, such as Admins and Members, each with their own tables and models.

Here's a step-by-step guide to achieve this:

  1. Create Separate Tables and Models:

    First, ensure you have two tables, e.g., users for Admins and members for Members. You should also have corresponding Eloquent models, e.g., User and Member.

  2. Define Guards in auth.php:

    In your config/auth.php file, define separate guards for each user type. You can also define separate providers if needed.

    'guards' => [
        'admin' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
    
        'member' => [
            'driver' => 'session',
            'provider' => 'members',
        ],
    ],
    
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],
    
        'members' => [
            'driver' => 'eloquent',
            'model' => App\Models\Member::class,
        ],
    ],
    
  3. Create Authentication Controllers:

    Create separate controllers for handling login for Admins and Members. You can use Laravel's Auth facade to specify which guard to use.

    For Admins:

    namespace App\Http\Controllers\Auth;
    
    use App\Http\Controllers\Controller;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Auth;
    
    class AdminLoginController extends Controller
    {
        public function showLoginForm()
        {
            return view('auth.admin-login');
        }
    
        public function login(Request $request)
        {
            $credentials = $request->only('email', 'password');
    
            if (Auth::guard('admin')->attempt($credentials)) {
                return redirect()->intended('/admin/dashboard');
            }
    
            return back()->withErrors([
                'email' => 'The provided credentials do not match our records.',
            ]);
        }
    }
    

    For Members:

    namespace App\Http\Controllers\Auth;
    
    use App\Http\Controllers\Controller;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Auth;
    
    class MemberLoginController extends Controller
    {
        public function showLoginForm()
        {
            return view('auth.member-login');
        }
    
        public function login(Request $request)
        {
            $credentials = $request->only('email', 'password');
    
            if (Auth::guard('member')->attempt($credentials)) {
                return redirect()->intended('/member/dashboard');
            }
    
            return back()->withErrors([
                'email' => 'The provided credentials do not match our records.',
            ]);
        }
    }
    
  4. Define Routes:

    Define routes for the login pages and actions in your web.php file.

    Route::get('/admin/login', [AdminLoginController::class, 'showLoginForm'])->name('admin.login');
    Route::post('/admin/login', [AdminLoginController::class, 'login']);
    
    Route::get('/login', [MemberLoginController::class, 'showLoginForm'])->name('member.login');
    Route::post('/login', [MemberLoginController::class, 'login']);
    
  5. Create Views:

    Create separate login views for Admins and Members, e.g., resources/views/auth/admin-login.blade.php and resources/views/auth/member-login.blade.php.

  6. Middleware and Redirects:

    Ensure you have middleware to protect routes based on the guard. You can create custom middleware if needed or use the auth middleware with the guard specified.

    Route::middleware('auth:admin')->group(function () {
        Route::get('/admin/dashboard', function () {
            // Admin dashboard
        });
    });
    
    Route::middleware('auth:member')->group(function () {
        Route::get('/member/dashboard', function () {
            // Member dashboard
        });
    });
    

By following these steps, you can set up a multi-authentication system with separate user tables and login pages for different user types in a Laravel application.

1 like
agillapro's avatar

@Snapey Thanks for your input. I tend to agree with you and will most likely do this.

Playing devil's advocate, one of the reasons we were considering this is because the attributes for each set of "users" is wildly different. The dozens of attributes for members will all be null for admins, and admins will have their own sets of attributes. At the model level there will be lots of relationships for members that don't make sense for admins. I suppose I could scope those relations?

1 like
Tray2's avatar

@agillapro There is nothing stoping you from extracting those attributes to other tables, and put the foreign key on those tables instead of on the users table. The attribute then belongs to the user, and not the user to the attribute.

1 like
krisi_gjika's avatar

@agillapro you can create multiple models referencing the same table in different contexts, and boot a global scope to limit the results to the specific context

1 like
martinbean's avatar

The dozens of attributes for members will all be null for admins, and admins will have their own sets of attributes. At the model level there will be lots of relationships for members that don't make sense for admins. I suppose I could scope those relations?

@agillapro You could create a MemberProfile model or similar, to hold member-specific attributes like that.

1 like

Please or to participate in this conversation.