@jekinney has an excellent answer on this in this post https://laracasts.com/discuss/channels/laravel/best-laravel-packages?page=1
Multiple User Authentication - Best practice?
Hello. I'm building an app where I have three different users types, and I'd like to know the best practice for user authentication system setup. In my app I have three different user types:
- Suppliers - Able to add products, communicate with users who contact them
- Users - Able to buy products, send messages to Suppliers
- Admin - Able to approve products, handle disputes, see transactional data.
How should I set this up? Should I go with three different tables and authentication classes or a user role system?
Whats your thoughts on the way to go here?
Authentication is different from authorization.
You can have same table for authentication i.e. login in to the system. However, to authorize an action you will require a user-role system ( that can be a simple as the role being suppliers,users and admin in the user table to as complex as hierarchical RBAC).
Only reason i see that you may require separate tables is if different schema is required for each user type.
I read through the other thread you answered in and it seems like it's a bit advanced for me. I am really struggling with this authentication layer and how to get it right for my application. I read countless threads on roles and permissions but I can't seem to grasp whether or not this is the right use-case.
As a comparison my application is kind of like airbnb. Suppliers can output adds that are viewed through a search engine for the user. The user can browse, communicate with the suppliers and ultimately buy from the supplier. Both the suppliers and users (and our admin panel) will have access to different backends.
The suppliers will be more strictly followed up on, and they will require more extensive information for signup. How to go about solving this? Can a role/permission system be of use here or do I need to build a multi-user authentication system with different tables?
@Khare this one I need to think on some. Interesting aspect. Bump me if I forget.
So is this like a Saas app where suppliers pay but users use for free but require an account? Like Amazon selling from third parties? Suppliers have teams and/or other users?
@jekinney Correct. Suppliers pay through commission per transaction. In the future the idea is for suppliers to have teams / more users, but not sure if we can make that happen before launch.
If you can separate user routes by type then you can probably get away without an authorization system. Authorization comes into its own when, for instance, a user role can edit a record whereas another user can use the same controller and views but have no rights to change anything, or can edit but not create new entries etc.
I would probably start out minimal and have a simple role column on the user model. This could contain 'client', 'supplier' and 'admin'
You can then create methods on the user model like isClient and isSupplier that just check the column.
Finally, create middleware that uses these methods to decide if the logged in user can access a particular route.
One area that might be an issue is if someone with a login wants to be both a supplier and a client. In this case, one column would not do it and you would end up with separate boolean columns for isClient and isSupplier.
Assumptions: Guest = Anyone not authenticated Member = A buyer of products Supplier = An entity/person selling products Admin = A person with full access to admin panel Admin account will be created by another admin. So set username and email, send an email and either have that guest create a password or provide a temporary password. The same code with minor tweaks can be used to set up a supplier's team of users too. so if done right, you may call a different function but call in other protected functions to do the basic work.
This can get complicated in a forum setting so bear with me and this is my opinion for getting it done and refactor later as you add updates and features: I like making sure the backend is set up for all features that I know of, so even it the feature isn't fully implemented at least the db schema and models are set (hopefully) which leads to IMO less issues later.
I would set up the roles/permissions I outlined in the post @jlrdw posted above but modify it for your requirements.
Roles
'slug' => 'system_admin'
'slug' => 'supplier_owner'
'slug' => 'member'
Set up permissions that each section needs, probably the member role may not need any permissions? For registration I would, depending on what type of role the guest desires, to separate registration forms that post to different methods/controllers. You can reuse any part of the form that is repeated or copy and paste. Do to possible changes I would copy and past for now till things are locked in and more than likely no other changes.
Once a guests logs in, check role and redirect accordingly to the page they need. If you have a frontend that guests and members can see products like any ecommerce, separate that in your view file. If the admin and supplier panel share the same theme, make a panel theme but separate admin and supplier views in separate folders. Use partials for common aspects and permission checks for displaying menu items they may have access to.
For teams I tend to make a supplier owner create a group (user specific permissions get messy quick) and assign the role supplier permissions to that group.
Permission Example
'slug' => 'add_products'
'slug' => ' edit_products'
'slug' => 'add_product_images'
'slug' => 'edit_product_prices'
Now the tricky part really kicks in: How do you establish which supplier account has access to what supplier efficiently. I have tried a three way pivot table with (in your case):
columns:
supplier_id, user_id, role_id
But this broke down do to member and admins. What I do is add a integer column to the role table that defaults to 0:
$table->increments('id');
$table->string('slug', 60)->index();
$table->string('name', 60);
$table->string('description', 500);
$table->integer('supplier_id')->unsigned()->defaults(0);
$table->timestamps();
When you create a global role the supplier id is 0, so add that check through the relation to the user table to see if greater then 0, is so find out which supplier and return that supplier information.
public function hasSupplierRole($supplierId)
{
foreach ($this->roles as $role)
{
if ($role->supplier_id == $supplierId)
{
return $role->supplier_id;
}
}
return false;
}
I have gone further to make a bit more dynamic on register of supplier by creating a new role each registration:
Permissions Table
$table->increments('id');
$table->string('slug', 60)->index();
$table->string('name', 60);
$table->string('description', 500);
$table->boolean('is_supplier_permission')->defaults(0);
$table->timestamps();
Assuming you have some type of slug for a supplier in your database and is unique,
Register new user as supplier
$user = User::create($request->all());
$supplier = Supplier::create($request->all()); // simplified as this isn't too important
$role = Role::create([
'slug' => $suppler->slug,
'name' => $suppler->name,
'supplier_id' => $supplier->id,
]);
$permissions = Permission::where('is_supplier_permission', 1)->get()
foreach($permissions as $permission)
{
$role->permissions()->attach($permission->id);
}
$user->roles()->attach($role->id);
Obviously the creating part needs cleaned up etc, but the idea is the same.
To get team members:
class Supplier extends Model
{
public function team()
{
return $this->hasMany(Role::class, 'id', 'supplier_id');
}
}
$supplier = Supplier::with('team', 'team.users')->find($id);
// should eager load all roles with the supplier_id and associated users for each role
Please or to participate in this conversation.