Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use multiple Auth guards for one Policy

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?

like image 921
Andy Noelker Avatar asked Jul 25 '17 18:07

Andy Noelker


People also ask

What is an authentication guard?

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.

What is Auth guard in Laravel?

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.


1 Answers

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.

like image 63
Andy Noelker Avatar answered Sep 29 '22 05:09

Andy Noelker