Today I had to lock down Laravel Horizon on one of my client APIs. No frontend, no users table, no auth system at all. Just Horizon running in production and, as usual, publicly accessible.
Crap!
The goal was simple: protect the Horizon dashboard with minimal setup. No database, no middleware, no extra complexity. Just basic authentication.
Laravel Horizon already gives us an authorization hook. We can use that to add HTTP Basic Auth and keep everything inside the HorizonServiceProvider.
All changes live here:
app/Providers/HorizonServiceProvider.php
Probably in your default horizon setup, you see a gate method in your HorizonServiceProvider.php, in order to swap the gate with a simple Basic Auth we need to replace this method with authorization method with the following logic.
Horizon::auth(function ($request) {
$username = config('horizon.auth.user');
$password = config('horizon.auth.password');
if (
empty($username) ||
empty($password) ||
$request->getUser() !== $username ||
$request->getPassword() !== $password
) {
header('WWW-Authenticate: Basic realm="Horizon"');
header('HTTP/1.0 401 Unauthorized');
exit;
}
return true;
});
If the credentials don’t match, the browser shows a basic auth prompt. If they do, Horizon loads normally.
No middleware. No users table. No database.
Configuration and Environment variables
Now lets setup the configuration variables. this could live in any configuration file, in my example above I’ve just added this snipped to config/app.php, but feel free to adjust this to your needs.
'auth' => [
'user' => env('HORIZON_USER'),
'password' => env('HORIZON_PASSWORD'),
],
Add the credentials to your .env file:
HORIZON_USER=your-username
HORIZON_PASSWORD=your-strong-password
Make sure they’re set in production as well.
I hope this can help some of you out there.
