Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

thetanaz's avatar

Is this Auth pattern safe?

So I'm currently building an ecommerce site for PC hardware, and I need to have both an User and an Admin portal. The users are the customers of the site while the admins have their own UI where they update stock, pricing and respond to customer support requests. So far so good. So I was wondering how I would split the auth logic and my first idea was with a role column in the users table but that felt a bit too close to the user logic so instead GPT suggested I add an Auth Guard named admins and a separate table.

So here's what I did:

  1. Add the admins provider in config/auth.php
 'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => env('AUTH_MODEL', App\Models\User::class),
        ],

        'admins'=> [
            'driver' => 'eloquent',
            'model'  => App\Models\Admin::class
        ],
    ],
  1. Make an admin guard:
'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'admin' => [
            'driver' => 'session',
            'provider' => 'admins',
        ],
    ],

And then I was getting error because my Admin model was just extending Models, and I had to change it to :

class Admin extends Authenticatable .

And finally I made separate Admin Controller for login etc.

Is this following best practices? From my testing it seems to work fine but I don't know if guards were meant to be used that way.

edit: Forgot to mention how the login logic executes: in AdminController:

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


        if (Auth::guard('admin')->attempt($credentials)) {
        
            $request->session()->regenerate();

          
            return redirect()->intended('/');
        }

      
        return back()->withErrors([
            'username' => 'The provided credentials do not match our records.',
        ]);
    }

So I use the Auth::guard('admin) command which seems to work fine.

0 likes
12 replies
Tray2's avatar

I would say that it depends, if you only have two roles, then it's fine to just add an is_admin column to the users table. If you feel that it's a bit too close as you put it, just create a table called admin_users and do a foreign key with the id of the user (user_id).

thetanaz's avatar

@Tray2 Yeah but Admin users are not going to be "users" in the sense that their accounts can't be used for purchasing stuff on the site, they are going to login in an admin panel only that's why it didn't seem natural to me.

Tray2's avatar

@thetanaz A user is a user regardless of the role, the user if for authentication, and the role is for authorization. In your case it doesn't really matter, but I wouldn't do a full role based setup if I only have two roles, user and admin.

1 like
thetanaz's avatar

@Tray2 Yeah you make a great point, I think I'm overcomplicating my logic for the sake of it being.. well complicated. Perhaps I should just simplify things.

Merklin's avatar

@Tray2 Even with adding the is_admin column to users, is it worth introducing an additional guard admin and use it for the admin routes? Can this be considered as an additional layer of security?

1 like
Tray2's avatar

@Merklin I would probably just put an admin middleware on the admin routes that check the is admin field.

1 like
thetanaz's avatar

I think in my case the guard is kinda sorta valid for the simple reason that normal users have different field (name,email, phone, address etc) while admins have only username and password, so they're 2 different data models. I had a hard time adjusting the redirect url for the admin guard tho, but at the end I just made another middleware and that worked instead of using ["auth.admin"] I'm using the new middleware.

martinbean's avatar

so instead GPT suggested I add an Auth Guard named admins and a separate table.

@thetanaz Please don’t. Multiple guards are awful to work with.

Just create routes for your customer account area and admin panel, and use middleware to restrict each route group based on the role(s) the authenticated user has:

Route::middleware(['auth', 'role:customer'])->prefix('account')->group(function () {
    // Customer account routes...
});

Route::middleware(['auth', 'role:admin'])->prefix('admin')->group(function () {
    // Admin panel routes...
});

If a user with only the customer role tries to access any admin panel routes, then you should return a 403 Forbidden response.

Even the example in the Laravel middleware docs uses role checking: https://laravel.com/docs/middleware#middleware-parameters

I‘ve also written a blog post covering user roles, given just how often the topic comes up on this forum alone: https://martinbean.dev/blog/2021/07/29/simple-role-based-authentication-laravel/

2 likes
thetanaz's avatar

@martinbean I've not used prefixes yet, but I can have admin.mysite.com use the role middleware and then the rest of the site can be without a prefix without conflicting with the admin prefix routes? What I mean is I don't want to have to have a "customer" prefix for the user routes it's unsightly.

Snapey's avatar

@thetanaz you are overcomplicating things again.

The prefix in Martins example is not a subdomain, its just a prefix to the url, eg mysite.com/admin/sales for instance, so that you dont need to repeat admin or the middleware on every route definition

thetanaz's avatar

@Snapey Ah you are correct, I misunderstood, right his example seems much more straightforward then my implementation. I'll just do what Martin suggested.

1 like

Please or to participate in this conversation.