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

lara28580's avatar

Authentication for 3 types of users

I wanna build authentication for 3 types of users admin, user, business and I dont know if I should build that on my own or I should use a package for this task. Option 1 is to build it like so:

https://laracasts.com/series/whats-new-in-laravel-5-1/episodes/16

but it feels like reinventing the wheel.

Option 2 is to use one of these packages:

https://github.com/JosephSilber/bouncer

https://github.com/spatie/laravel-permission

But I am concerned using such packages because what if support runs out? Any advice?

0 likes
24 replies
bugsysha's avatar

Search on the forum. There is a bunch of discussions on this topic. Do not go with anything complex, just place the role or type column on users table and there is no need for 3 different types of authenticated users. They are all users of your system, they just have different permissions based on a role/type.

2 likes
lara28580's avatar

Yeah thanks for your answer! So you mean adding a type column to my existing user is completely enough? So type gets then admin or business or user then I have to check with gates?

lara28580's avatar

Or maybe better to use middleware then because the actions they can do are completely different ?

bugsysha's avatar

So you mean adding a type column to my existing user is completely enough?

More than enough.

See what makes sense for you. Middleware is faster solution to implement. And leave gates for per user specific permissions if you ever get to that point in your project.

lara28580's avatar

I am really trying to figure out how to solve this. If I store the type of a user in the users table how would I check for a certain action. I wanna make this in the most common way or standardized way.

How would it look like in my Model? What I thought it could look like?

User model

public function isAdmin($user) {
    return $this->type == $user->type;
}
lara28580's avatar

The problem is if I use middleware I am not able to use gates anymore because the route is not reachable anymore for every person?

bugsysha's avatar

Change method to this.

protected const TYPE_ADMIN = 'admin';

public function isAdmin(): bool
{
    return $this->type === static::TYPE_ADMIN;
}

The problem is if I use middleware I am not able to use gates anymore because the route is not reachable anymore for every person?

You can use gates to filter out specific user type/role. If you have admin middleware applied, then you might have some admins which are not allowed to do something, and for that you can use gates.

1 like
jlrdw's avatar
//spatie example:
public function update(Request $request, Post $post) {
    if ($post->author !== auth()->user()->id || auth()->user()->cannot('edit posts'))
        abort(404);// or some other 
    }
}

Also see: https://gist.github.com/jimgwhit/ed44a6c81815804f1ab910ce9eb88d84

But in your case I'd use spatie, it's well maintained.

RBAC and query scopes working together is one way to narrow down a query.

For example a situation where if admin all records are shown, but if regular user only the users records are shown.

So a query scope is where to fine tune these things.

Permissions are assigned to roles.

  • admin can do certain things
  • bookkeeper can do certain things

So if Bob has only admin role and logs in he can do admin stuff.

Sally has both roles, logs in, can do both.

However when Bob is logged in, Bob cannot be fumbling and messing around with bookkeeping.

You have to remember, some folks will have duel roles.

Bob can however view general user data and edit if needed as an admin.

Keep it simple, just ask yourself this.

What can and cannot the logged in user do and not do.

Remember also

RBAC is dual, methods protected, and ensuring someone did not change something in the url.

Example

somesite/articles/edit/5

but what if user changes the 5 to another number?

That's where lines of code like (just example)

if (! $article_id === Auth::user()->id) {
            //  redirect somewhere or abort or whatever you do.
        } 

If a user did change the 5 to 25, and 25 does not belong to them a redirect happens.

There is a lot to RBAC, take time to learn it and setup correctly.

1 like
lara28580's avatar

Thanks @jlrdw ...but I think spatie is a little bit overkill for my application? I have 3 user types admin, user and business and each of them has their own permissions. So business can only do business stuff but not user stuff and vice versa but an admin can do all. So to keep it very simple would it not be better to user middleware groups for each user_type? Maybe I dont get the idea...

@bugsysha should I do that for every user type?

protected const TYPE_BUSINESS = 'business';

public function isBusiness(): bool
{
    return $this->type === static::TYPE_BUSINESS;
}
jlrdw's avatar

Read edited reply, (url protection). It doesn't matter what RBAC you use, all are similar:

The main thing is:

What can that logged in user do or not do.

1 like
lara28580's avatar

I think I will use the package of spatie... how do you init the roles for example the admin role? In the AppServiceProvider ?

jlrdw's avatar

@smoketm take your time here, RBAC is not a "quick setup". Spatie has many examples and documentation.

I'd make a test project and just work some of the examples.

1 like
jlrdw's avatar

Put another way, don't look at it like:

Authentication for 3 types of users

Instead look at it like:

  • Okay an authenticated user has logged in
  • Now what can they do or not do with the role they have

So just look at things like:

Authentication for users. Of course after a login, you can redirect to various places depending on role.

  • user - redirect to a user dashboard
  • admin - redirect to an admin dashboard
  • maintenance - redirect to a maintenance dashboard
  • bookkeeper - redirect to accounting dashboard

Duel roles bookkeeper and admin

Work out how to handle a duel role dashboard, part of being a developer, to solve, overcome.

1 like
lara28580's avatar

Really hard to understand how to hook up those roles to assign it to specific users on registration and how to check for certain actions cant find that anywhere

Edit: Is this the way to go in production to set the roles in a seeder to use it then at the registration? Like here: https://docs.spatie.be/laravel-permission/v3/basic-usage/new-app/

Maybe it is better to use an event handler?

I am looking for the most standard way to do this

bugsysha's avatar

Is this the way to go in production to set the roles in a seeder to use it then at the registration?

No, cause bunch of users bumps into a wall with this package. You can search the forum and see what are the issues and how many of them have it.

Maybe it is better to use an event handler?

I wasn't following this discussion closely so I'm not sure what do you want to use event handlers for?

I am looking for the most standard way to do this

The most standard way is the way I've described. If you need only a basic way to distinguish user's permissions then do not overcomplicate things with packages you do not need.

jlrdw's avatar

There isn't a most standard way.

Some people catch permissions in The View some routing me I like catching right at the method level (function).

I showed a Spatie example of catching if someone could or could not do something at method level.

Who assigns these roles is it the administrator.

Have you set up your related roles table yet.

Me instead of a related table I just added the field role and use a comma separated list.

Have you watched any of Jeffrey's free videos on Authentication.

Remember spatie works with laravel role and permissions.

jlrdw's avatar

Also the spatie doc has links to all sorts of usages. Are you on a mobile device and can't see their links.

For example https://docs.spatie.be/laravel-permission/v3/basic-usage/role-permissions/

But there are several other links showing different usages.

You really need to browse through much of this documentation not just one thing.

Remember RBAC it's not ever going to be a simple solution rather it's spread out throughout your application and it is a very steep learning curve.

Even Jeffrey in a video states that it is tricky to learn but becomes easier as you use it.

Which is one of the reasons I like checking stuff at the method level, to me it's the easiest way to use roles.

lara28580's avatar

@bugsysha I did it like you said and set up a type for every user on registration. What I dont know now what is if admin and lets say a business user should access the same route? If I pass admin and business to the same middleware group I get redirected because admin middleware restricts business users to access the same route?

Something like that:

Route::group(['middleware' => ['admin' , 'business']], function () {

//routes here
bugsysha's avatar
bugsysha
Best Answer
Level 61

I guess admin has more premissions than business so admin can access everything?

// business middleware
public function handle($request, Closure $next)
{
    $user = $request->user();
    if ($user->isBusiness() || $user->isAdmin()) {
        return $next($request);
    }

    return redirect('home');
}

// admin middleware
public function handle($request, Closure $next)
{
    if ($request->user()->isAdmin()) {
        return $next($request);
    }

    return redirect('home');
}

// routes
// where you want business and roles with greater permissions to have access then just put business middleware
Route::group(['middleware' => ['business']], function () {

// where you want admin to have access then just put admin middleware
Route::group(['middleware' => ['admin']], function () {

Do not forget auth middleware.

There are also other ways to do this, but this is the cleanest and easiest so you do not forget anything to add.

1 like
lara28580's avatar

Thanks for the fast answer I did it like so now

public function handle($request, Closure $next)
    {
      if(auth()->user() && auth()->user()->isAdmin()) {
        return $next($request);
      } elseif (auth()->user() && auth()->user()->isBusiness()) {
            return $next($request);
        }

      return redirect('/');
    }

Have admin and business middleware seperated

Please or to participate in this conversation.