horizon: Gate in production does not work (403)

I’m using Laravel 5.7 with Horizon.

It works in local, but if i set production in my .env file on our server, it gives me the 403 status code.

This is my service provider:

    protected function gate()
    {
        Gate::define('viewHorizon', function ($user) {
            return true;
        });
    }

Do i need to register this gate somewhere? I’m logged in as a user on my application.

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 9
  • Comments: 25 (4 by maintainers)

Most upvoted comments

I had the same problem (Laravel 5.8, Horizon 3.2.2)

My issue was this default code in the published HorizonServiceProvider:

        Gate::define('viewHorizon', function ($user) {
            return true;
        });

In my case I’ve no auth middleware because it’s done on the webserver already via IP-addresses matching VPN, etc.

So, no authenticated user present.

However: when defining the closure like this function ($user) {… } and there is no authenticated user, this gate is entirely skipped because of this code https://github.com/laravel/framework/blob/9b7520a2ac0009d4c1ff2322e3ef673ef702e7e2/src/Illuminate/Auth/Access/Gate.php#L527-L528

The canBeCalledWithUser performs PHP reflection on the closure and if there’s no auth user but $user is present, it will not execute the gate.

The solution: change the closure to have user optional: function ($user = null) { … }

You have to register Horizon’s service provider. In config/app.php:

   'providers' => [
        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
    ...
        App\Providers\TelescopeServiceProvider::class,
        App\Providers\HorizonServiceProvider::class,
    ],

I am using a non-default guard since my default guard is for my API and uses JWT tokens. So I had some trouble getting this to work correctly. In the end this is what works:

I created a login form using laravel/ui, but I am using a custom login controller which looks like this:

$credentials = $request->only('email', 'password');

if (auth()->guard('web')->attempt($credentials)) {
    return redirect()->intended('home');
}

Then in my horizon.php config I set this for my middlewares:

'middleware' => ['web', 'auth:web'],

Note that I specify that the auth middleware should use the web guard since that is not the default

And finally in my HorizonServiceProvider I have this

Gate::define('viewHorizon', function ($user = null) {
    return in_array(\Auth::guard('web')->user()->email, [
        'example@gmail.com',
    ]);
});

In config/app.php:

   'providers' => [
        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
    ...
    ...
        App\Providers\RouteServiceProvider::class,
        App\Providers\HorizonServiceProvider::class
    ],

HorizonServiceProvider must always be listed after RouteServiceProvider to make gate() work correctly.

I had the same problem (Laravel 5.8, Horizon 3.2.2)

My issue was this default code in the published HorizonServiceProvider:

        Gate::define('viewHorizon', function ($user) {
            return true;
        });

In my case I’ve no auth middleware because it’s done on the webserver already via IP-addresses matching VPN, etc.

So, no authenticated user present.

However: when defining the closure like this function ($user) {… } and there is no authenticated user, this gate is entirely skipped because of this code https://github.com/laravel/framework/blob/9b7520a2ac0009d4c1ff2322e3ef673ef702e7e2/src/Illuminate/Auth/Access/Gate.php#L527-L528

The canBeCalledWithUser performs PHP reflection on the closure and if there’s no auth user but $user is present, it will not execute the gate.

The solution: change the closure to have user optional: function ($user = null) { … }

This solution worked . I am using Auth::guard(‘guard’)->user().

Thanks.

It is possible that you are not using the default guard。change the default guard in config/auth.php

such as

    'defaults' => [
        'guard'     => 'admin',
        'passwords' => 'front_users',
    ],

I can confirm this worked for me:

Gate::define('viewHorizon', function ($user = null) {
            $u = Auth::guard('web')->user();
            return in_array($u->email, [
                'my@email.com'
            ]);
        });

…combining the null user and then manually setting the guard.

Hi there!

In the beginning you should check that in your .env file environment variable APP_ENV=production or gate will won’t to work.

Here is the way how I can solved it:

  • in config/auth.php you need to change the default guard from api to e.g. web:
    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],
  • the next step is to change the gate function like I did (note that $user should be nullable):
protected function gate(): void
    {
        Gate::define('viewHorizon', function ($user = null) {
            return Auth::guard('moonshine')->user()->hasRole("Admin");
        });
    }

don’t forget to change guard to your own.

On my other project gate wants to work, seems like Gate::define not working, I’m solved problem by checking a role for user in the authorization function to:

/**
 * @return void
 */
protected function authorization(): void
{
    Horizon::auth(function ($request) {
        return Auth::user()->hasRole('your_role_here');
    });
}

I am using a non-default guard since my default guard is for my API and uses JWT tokens. So I had some trouble getting this to work correctly. In the end this is what works:

I created a login form using laravel/ui, but I am using a custom login controller which looks like this:

$credentials = $request->only('email', 'password');

if (auth()->guard('web')->attempt($credentials)) {
    return redirect()->intended('home');
}

Then in my horizon.php config I set this for my middlewares:

'middleware' => ['web', 'auth:web'],

Note that I specify that the auth middleware should use the web guard since that is not the default

And finally in my HorizonServiceProvider I have this

Gate::define('viewHorizon', function ($user = null) {
    return in_array(\Auth::guard('web')->user()->email, [
        'example@gmail.com',
    ]);
});

Specifying auth:web in the horizon config is what I was missing. I also realized the HorizonServieProvider wasn’t added automatically to config/app.php Since web wasn’t my default auth guard. Thanks

You have to register Horizon’s service provider. In config/app.php:

   'providers' => [
        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
    ...
        App\Providers\TelescopeServiceProvider::class,
        App\Providers\HorizonServiceProvider::class,
    ],

Thank you! This took me hours to find. I don’t think it’s in the docs, and I don’t know why.

解决方案:更改闭包以使用户可选: function ($user = null) { … } Official Repair to Reduce User Difficulty