API Auth for GraphQL in Laravel

Josh Krawczyk
Feb 22 · 5 min read

I will demonstrate how you can get all client requests (even authentication) to go through your single GraphQL endpoint. This will allow you to only need one client package for handling communication to your API from your web app. I will migrate the Laravel Authentication scaffolding to GraphQL and will be using Passport to handle the API tokens. Then I will convert my VueJS frontend to use only GraphQL. I am utilizing Lighthouse GraphQL for the server-side graphql endpoint.

You can find the full code example here on Github. Be sure to use the graphql-demo branch.

I am building this demo off of another article. If you need the basics on GraphQL check it out!

Auth Flow

I will be using Personal Access Tokens (PAC)for this demonstration. See the Laravel Docs for more on those. Once the user submits the credentials from the login page, we will see if they are valid. If so, we will create a PAC and return an encoded JWT. Passport will be able to decode this JWT and extract the PAC. I will then return a Http-Only cookie with this JWT and Laravel will encrypt it.

From the web app’s perspective, it doesn’t have to worry about any tokens because the cookie will get passed to Laravel on every request. I will have a Middleware that will handle this cookie and pass it over to Passport in a format that it understands. More on that later.

Let’s get started!

Setup

I will be utilizing a Lighthouse API Authentication package to handle most of the heavy lifting. This is a Multi-Tenant example so I will change a few things to work around that. Make sure you have Passport setup before moving on.

Add the auth package as a composer dependency.

composer require joselfonseca/lighthouse-graphql-passport-auth

Now publish the graphql schema and configuration file.

php artisan vendor:publish --provider="Joselfonseca\LighthouseGraphQLPassport\Providers\LighthouseGraphQLPassportServiceProvider"

This will create a schema file in graphql/auth.graphql

Now we need to modify the AppServiceProvider.php to make sure that if a tenant is identified it will force the database connection to tenant. This will force passport to use our tenant databases versus the system.

This is my AuthServiceProvider.php. This is a pretty standard Passport configuration and is just here for reference. Notice I am limiting the lifetime of the Personal Access Tokens. The default lifetime is one year. You can set this to whatever you need.

AddAuthHeader.php Middleware

I created a custom middleware that will take the JWT from the cookie and set the Authorization header in the incoming request. I opted to do this so I didn’t have to create a custom authentication guard. I tried using Laravel’s CreateFreshApiTokens middleware, but that depends on having a persistent session which won’t work with our API. In only a few lines of code I replicated the CreateFreshApiTokens middleware and I didn’t have to mess with tokens on the client. How cool is that?

This will first check if there is an Authorization header and if not it will do a check if the _token is present. If it is present it will add the Authorization header to the request with the JWT and carry on. Note the the Cookie will be unencrypted at this point in the request.

Modifications to Kernel.php

Just add EncryptCookies, AddAuthHeader, and AddQueuedCookies Middleware to the api group . This will allow us to Queue a cookie during a GraphQL resolver and it will attach it to the response. This is necessary because with GraphQL we won’t have control over the response object that is returned. The order matters!

The order matters for the api middleware

Auth Directive

In our GraphQL schema, we need a mechanism to protect queries and mutations. We can use the Middleware directive, but any exceptions that are thrown will just show a generic Internal Error message. I created a custom directive that mimics the regular auth Middleware that Laravel provides.

Create a folder in app called GraphQL and put a Directives folder in it. Now create a file called ProtectDirective.php and save the below contents.

There is a ton going on here! You don’t have to understand everything that is happening in this file. Just know that you can use @protect(guards: [“api”]) in you schema file, and is the equivalent to using the api:auth middleware.

GraphQL Schema

I like moving the root graphql folder to routes folder, but you can keep it there if you like. Just know that if you do move it you will need to update the lighthouse-graphql-passport.php config file.

Here is my updated schema.

Notice the @protect directive on the root Query and Mutation types.

I modified the auth.graphql file to remove the refresh/access token mutations and protected the logout mutation. I also added a mutation for registering users.

LoginResolver.php

We will need to create a custom login resolver since we aren’t using access/refresh tokens to authenticate.

Create the folder structure app\GraphQL\Resolvers.

Create a file called LoginResolver.php and put in the below contents

This will check if the user credentials are validate. I use the Auth::once method so that no session data is stored. If the credentials are invalid I will throw an Exception which is returned as a GraphQL Error.

Next I will check to see if a _token cookie is already present and if not I will create one. The createToken method (From hasApiTokens trait) will create and save a Personal Access Token. However, it will return a JWT instead of the actual PAC. This is perfect, because the Authorization header needs a JWT and and authentication will fail if it is the straight up Access Token

LogoutResolver.php

To logout, we will revoke the Personal Access Token and delete the cookie. The next time the web app tries to authenticate it will get an authentication GraphQL error and redirect to the login page.

That is it for the server side! You can now remove the Auth Controllers and relevant web/api Routes from your application. Now we will change the Vue app to use the new Auth configuration

The Frontend

There isn’t too much we need to modify here. All of the heavy lifting for the authentication is handled in Laravel. We just have to move to using Vue Apollo instead of axios for our authentication. This just involves creating graphql client queries and updating the components to use Vue Apollo for auth.

Apollo Configuration

Here is the Apollo configuration I am using. Nothing special here. I’m just redirecting to the login path on when the server throws an Unauthenticated Exception.

Auth Queries

In the queries folder in your js root, create an auth.gql file. Put in the following content. We will import these queries into our Auth module. We can also use these directly in our components.

Auth Plugin

For this demonstration I used the Vue plugin system to create my auth module. You don’t have to do it like this, but I felt it made things cleaner.

Create this folder structure in your js root plugins\vue-auth-graphql\. Now create an index.js in there and past the following content.

This plugin is basically a wrapper around Apollo and makes the methods available using this.$auth.

App.js

Now we can import all of our modules! Here is my app.js file for reference.

Login Component

There isn’t anything too crazy going on here. Just using the Auth module to login.

Conclusion

That is it! I moved all of the other Auth Components over as well. You can see those in the repo. I feel like this greatly simplifies the API authentication flow and provides the best of both worlds with usability and security.

Josh Krawczyk

Written by

Systems Engineer by day, Programmer by night. Just doing my best to juggle life, family, career, and passion.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade