How To Use Laravel Horizon With a JWT Token

Image Credit: Mohammad Rahmani on Unsplash

Laravel Horizon makes managing and monitoring queues very easy. After installing the package, you can simply visit /horizon from your app URL to see the Horizon Dashboard where you will be able to start managing and monitoring your Laravel queues.

The package also has authorization built-in, so you can configure it to only allow specific users to access the Horizon dashboard.

This is done using an authorization gate function within the app/Providers/HorizonServiceProvider.php file. This function controls access to the /horizon route in non-local environments. (The authorization function does not apply in a local environment.)

/**
 * Register the Horizon gate.
 *
 * This gate determines who can access Horizon in non-local environments.
 *
 * @return void
 */
protected function gate()
{
    Gate::define('viewHorizon', function ($user) {
        return in_array($user->email, [
            '[email protected]',
        ]);
    });
}

Laravel automatically injects the authenticated user into the gate closure.

This works great in a standard Laravel setup.

However, a problem arises when you have Laravel (as your backend) running on one subdomain, and your frontend (maybe a Single Page Application) running on another;

  • api.example.com - Laravel Application

  • app.example.com - SPA (Built with Vuejs, React, etc)

In this kind of setup, token-based or stateless authentication is generally used, but Laravel Horizon expects a stateful or session-based authentication by default.

Getting this to work involves two steps;

1. Update the Horizon middleware

Within your config/horizon.php configuration file, update the middleware option to include the middleware your App uses to authenticate users. In my case, I use tymon/jwt-auth. So, after updating my User model and Configuring the Auth guard as described in the documentation. I updated the middleware option in my config/horizon.php like so;

// ...

'middleware' => ['api'],

// ...

2. Cookies & Authenticated Requests

Tymon/jwt-auth allows for a number of ways to send a JWT token via HTTP in order for requests to be seen as authenticated. One of these methods is by setting a cookie.

So, getting Laravel Horizon to work with a JWT token is as simple as saving the JWT token received when authenticating a user in a cookie, specifically named token, while making sure to prepend the cookie domain with a dot when setting the cookie (E.g .example.com)

Cookies.set('token', 'eyJhbGciOiJIUzI1NiI...', {
  domain: '.example.com',
});

Prepending the domain with a dot (.) allows the cookie to be accessible to all subdomains.

Ideally, the way this should work is when a user visits your SPA (app.example.com) and enters their login credentials, you send a request to your Laravel application (api.example.com) to authenticate the user. Laravel with the help of tymon/jwt-auth authenticates the user and returns a JWT token. You then save the returned JWT token in a cookie with the name token similar to the code above.

And that's it. Now when the user visits api.example.com/horizon, Laravel should now be able to pick up the authenticated user and pass it to the Horizon authorization gate function so that you can perform whatever check you need to.

Nuxt & GraphQL

If you are using Nuxt.js with @nuxtjs/apollo, the package already sets a cookie with the JWT token returned from your authentication server, so all you need to do is customize the apollo.tokenName and apollo.cookieAttributes.domain options in your nuxt.config.js file like so;

apollo: {
  // ...

  tokenName: 'token',

  cookieAttributes: {
    domain: '.example.com'
  },

  // ...
}

And that's how to use JWT token to authenticate with Laravel Horizon. If you have any questions, feel free to leave them in a comment below. ✌🏽