8
votes

Case: I'm building a forum using Laravel's Authorization as a backbone using policies. Examples of checks I run are stuff like @can('view', $forum), and @can('reply', $topic), Gate::allows('create_topic', $forum) etc. These checks basically checks if the users role has that permission for the specific forum, topic or post. That way I can give roles very specific permissions for each forum in my application.

The issue is that all of these checks go through the Gate class, specifically a method called raw() which in its first line does this:

if (! $user = $this->resolveUser()) {
    return false;
}

This presents an issue when dealing with forums. Guests to my application should also be allowed to view my forum, however as you can see from the code above, Laravels Gate class automatically returns false if the user is not logged in.

I need to be able to trigger my policies even if there is no user. Say in my ForumPolicy@view method, I do if(User::guest() && $forum->hasGuestViewAccess()) { return true }

But as you can see, this method will never trigger.

Is there a way for me to still use Laravel's authorization feature with guest users?

2

2 Answers

2
votes

I'm not aware of a super natural way to accomplish this, but if it's necessary, you could change the gate's user resolver, which is responsible for finding users (by default it reads from Auth::user()).

Since the resolver is protected and has no setters, you'll need to modify it on creation. The gate is instantiated in Laravel's AuthServiceProvider. You can extend this class and replace the reference to it in the app.providers config with your subclass.

It's going to be up to you what kind of guest object to return (as long as it's truthy), but I'd probably use something like an empty User model:

protected function registerAccessGate()
{
    $this->app->singleton(GateContract::class, function ($app) {
        return new Gate($app, function () use ($app) {
            $user = $app['auth']->user();
            if ($user) {
                return $user;
            }
            return new \App\User;
        });
    });
}

You could go a step further and set a special property on it like $user->isGuest, or even define a special guest class or constant.

Alternatively you could adjust your process at the Auth level so that all logged-out sessions are wrapped in a call to Auth::setUser($guestUserObject).

1
votes

I just released a package that allows permission logic to be applied to guest users. It slightly modifies Laravel's Authorization to return a Guest object instead of null when no user is resolved. Also every authorization check now makes it to the Gate instead of failing authorization instantly because there isn't an authenticated user.