I have implemented multiple Auth guards in a Laravel 5.4 project (one of for admins and the other for regular users). This has worked successfully so far and both admins and users are able to log in. I am now trying to implement a Policy class that works for both Auth guards. This is because I have certain models that I want all administrators to edit and only users who own the model to be able to edit. So I have defined a policy with this method.
App\Policies\ModelPolicy
public function update(User $user, Model $model)
{
if ($user->id === $model->user_id) {
return true;
}
if (Auth::guard('admin')->check()) {
return true;
}
return false;
}
Then in whatever controller method I have for my model:
App\Http\Controllers\ModelController
public function update(Model $model)
{
$this->authorize('update', $model);
// update model
}
This works perfectly if a regular user is logged in. However, when an admin user is logged in, it doesn't even reach the policy (I know this from error logging). I am guessing that the Policy class does something to automatically deny a request if the default guard in Auth::check()
fails. However, since it is valid for my users to have one of several guards (not just the default), I need to bypass this behavior.
I know I could implement the admin logic in my controller method and only use the policy if I know I am dealing with a non-admin:
public function update(Model $model)
{
if (!Auth::guard('admin')->check()) {
$this->authorize('update', $model);
}
// update model
}
However, this can quickly spiral out of control if my admin condition is more complicated than simply being logged in. More importantly, all of this logic belongs in a Policy, not muddying up my controller.
How is it possible to use the same Policy class for multiple authentication guards?
AuthGuard is used to protect the routes from unauthorized access. So here we are creating an AuthGuard in angular that will protect our routes from unauthorized access. Example: We can create an AuthGuard by running simple command using CLI. ng g guard services/auth.
At its core, Laravel's authentication facilities are made up of "guards" and "providers". Guards define how users are authenticated for each request. For example, Laravel ships with a session guard which maintains state using session storage and cookies.
I ended up overriding the authorize
method on the base controller class to make the correct Guard the default Guard. Then, the $user
argument passed into my policy will be an instance of whichever Auth guard the current user is logged in as.
app/Http/Controllers/Controller.php
use Auth
class Controller extends BaseController
{
use DispatchesJobs, ValidatesRequests;
use AuthorizesRequests {
authorize as protected baseAuthorize;
}
public function authorize($ability, $arguments = [])
{
if (Auth::guard('admin')->check()) {
Auth::shouldUse('admin');
}
$this->baseAuthorize($ability, $arguments);
}
}
Now that the Policy will be passed in either my User model or my Admin model, I need to make sure that I remove the type-hinting for the argument and check the type of the model that is passed in. I don't need to do any Auth::check()
because I know that the $user
that is passed in must be a logged in user of the type that I want.
App\Policies\ModelPolicy
use App\User;
public function update($user, Model $model)
{
if ($user instanceof User) {
return $user->id == $userId;
}
// Is an Admin
return true;
}
And now I have access to desired Auth guard to do whatever I want with it in my Policy.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With