I'm trying to access the logged in User so I can validate if the dashboard should be shown but inside the Horizon::auth callback all attempts to get the User from the request return null. I'm presuming this is because the session middleware hasn't fired yet? Has anyone else come across this and found a solution?
I've tried calling the auth method in the register and boot methods of my AppServiceProvider fyi.
Where you call Horizon::auth() doesn't matter; it's where it's executed that matters. As to answer that, it happens in the base controller of Horizon through $this->middleware(), which is not guaranteed to run before the session middleware.
Ok makes sense, is there a way to work around that, that you know of? Figured I'd ask before doing some digging myself.
If not seems like the auth method is a little pointless other than adding a query string 'key' to the URL and checking that through the method?!
Also adding to this the docs directly mention 'indicating whether the user should have access to the Horizon dashboard' slightly conflicting since the user is not available?
The simple answer to this is to move the routing to be more like how Passport does routing, making it as easy to control it's constraints as any other routes (via the registrar) using middleware.
ie, in Passport you can easily do this:
Route::prefix('api/oauth')
->middleware('api')
->namespace('\Laravel\Passport\Http\Controllers')
->group(function ($router) {
$registrar = new PassportRouteRegistrar($router);
$registrar->forAccessTokens();
$registrar->forTransientTokens();
});
At its core, the reason you can't access the user is because your auth middleware hasn't run, because the auth middleware isn't registered by Horizon, so there's no great way of solving it, other than calling your auth middleware from within the auth closure (as of now).
How would you call the middleware manually, I'm trying to prepend the middleware e.g:
$this->app->make('Illuminate\Contracts\Http\Kernel')
->prependMiddleware(StartSession::class);
Not sure if it's too late in the request though? I've tried this outside of the auth callback as well in both boot/register but still not getting anything in Auth::user().
Nevermind - I needed to tell Kernel to handle again. For anyone interested in how I got this to work here is my app's HorizonServiceProvider.
<?php
namespace App\Providers;
use Laravel\Horizon\Horizon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\ServiceProvider;
use Illuminate\Session\Middleware\StartSession;
class HorizonServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
//
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
if( request()->is('horizon', 'horizon/*') ) {
$this->app->make('Illuminate\Contracts\Http\Kernel')
->prependMiddleware(StartSession::class)
->handle(request());
}
Horizon::auth(function () {
return Auth::check();
});
}
}
Remember to add this to your list of providers in config/app.php.
If anyone has a better way happy to get feedback!
@scrwdnet a better way would be the following:
Horizon::auth(function () {
try {
return $this->app->make(YourAuthMiddleware::class)
->handle(request(), function ($r) { return true; });
} catch (HttpException $e) {
return false;
}
});
This will essentially run your auth middleware separately. Your issue btw, isn't that the session isn't started. StartSession is part of the web middleware which is already applied, that part should be fine. I think the issue is just that your auth middleware hasn't run yet, so even just running the kernel handle would be enough
I've made a PR to add support for specifying a middleware to use for Horizon, which is a WIP, but should be able to get that done today and hopefully get it merged, or get some useful feedback
I'm currently having a slightly different problem but I think it's related to this issue.
I was testing the Horizon::auth method adding a query string param key and checking inside the closure.
Ok, it works, but only for entering the dashboard. Subsequent (ajax) requests for retrieving info of the queues doesn't work because it doesn't append my key parameter to the query string (obviously, tbh), and it doesn't pass the Horizon auth middleware.
I don't know if it's on purpose but I think the auth horizon method is very limited to 'static' resources in the server (.env file, any key stored somewhere, etc...) but not for dynamic info (as sessions), which is a bit useless.
Hope @phroggyy PR solve this as soon as possible.
Closing since two PRs are tackling this at the moment :)
any updates on this?
both PRs were rejected it seems
After trying a couple of things simply adding the following to my service provider's boot method worked for me
\Horizon::auth(function ($request) {
return auth()->user()->isAdmin();
});
where isAdmin is in my User model
public function isAdmin()
{
return $this->admin;
}
I also had to add a 403.blade.php in /resources/views/errors/ otherwise it would throw a whoops exception
@vesper8 it works now because they have pushed version 1.* now which contained a fix for the original issue 馃憤馃徎
How can i access horizon by laravel passport? (tokn api middleware)
@fannyfan414
To authenticate user to horizon throuth passport, you have to create your own middleware.
You have to manually create the same mechanism used in the client (passing Bearer token in the Authorization headers)
The cleanest way (for me):
Edit (if you want) the cookie default name used by passport, in the AuthServiceProvider: _(app/Providers/AuthServiceProvider.php)_
public function boot()
{
$this->registerPolicies();
Passport::routes();
Passport::cookie('token');
}
Create CookieBearer Middleware: _(app/Http/Middleware/CookieBearer.php)_
namespace App\Http\Middleware;
use Closure;
use Laravel\Passport\Passport;
class CookieBearer
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($token = $request->cookie(Passport::cookie())) {
$request->headers->set('Authorization', 'Bearer ' . $token);
}
return $next($request);
}
}
Define middleware in your Kernel: _(app/Http/Kernel.php)_
protected $routeMiddleware = [
....
'cookie-bearer' => \App\Http\Middleware\CookieBearer::class,
];
Finally edit horizon config: _(app/config/horizon.php)_
'middleware' => ['cookie-bearer'],
And define your logic in gate HorizonServiceProvider: _(app/Providers/HorizonServiceProvider.php)_
protected function gate()
{
Gate::define('viewHorizon', function ($user) {
return $user->isAdmin() || $user->hasABigD**k();
});
}
Most helpful comment
both PRs were rejected it seems
After trying a couple of things simply adding the following to my service provider's boot method worked for me
where isAdmin is in my User model
I also had to add a 403.blade.php in /resources/views/errors/ otherwise it would throw a whoops exception