Horizon: Gate in production does not work (403)

Created on 3 Apr 2019  ·  20Comments  ·  Source: laravel/horizon

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.

needs more info

Most helpful comment

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) { … }

All 20 comments

Can you please fill out the issue template?

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,
    ],

Hi there,

Thanks for reporting but it looks like this is a question which can be asked on a support channel. Please only use this issue tracker for reporting bugs with the library itself. If you have a question on how to use functionality provided by this repo you can try one of the following channels:

Thanks!

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) { … }

@mfn might be worth a pr to the docs if you have some time :)

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

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 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.

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, [
        '[email protected]',
    ]);
});

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.

@ryancwalsh that's because these lines get added when you run php artisan horizon:install so the docs just assume you will run this command

@vesper8 That's not my experience. I've run php artisan horizon:install multiple times (and even just tried it again after reverting my manual changes to config/app.php), and my config/app.php does not mention Horizon at all unless I add App\Providers\HorizonServiceProvider::class, manually.

@ryancwalsh could it be that your config file is customized to a degree that the installer can't find the anchors it's trying to install against?

ie: it adds its content by doing a string replacement, but must find the string in the first place:
https://github.com/laravel/horizon/blob/3.0/src/Console/InstallCommand.php

@drbyte Interesting. No, I think my config/app.php looks pretty normal. I see App\Providers\EventServiceProvider::class, in mine, which is what https://github.com/laravel/horizon/blob/3.0/src/Console/InstallCommand.php#L63 seems to be looking for. The double backslashes \\ aren't intuitive to me, but I assume that file has those intentionally.

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, [
        '[email protected]',
    ]);
});

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

I also ran into not having HorizonServiceProvider installed. My app initially integrated with Horizon 1.0, which does not seem to require this, and the upgrade guide to v3 did not mention it.

The other gotcha I ran into was needing to change the default production PHP 7.4 php.ini to allow pcntl_async_signals, pcntl_alarm, and pcntl_signal.

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.

APP_ENV!=local
to make gate() work correctly.
APP_ENV=production

I can confirm this worked for me:

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

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

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mahdiahmadi73 picture mahdiahmadi73  ·  3Comments

meathanjay picture meathanjay  ·  3Comments

francislavoie picture francislavoie  ·  5Comments

sojeda picture sojeda  ·  5Comments

RicardoRamirezR picture RicardoRamirezR  ·  3Comments