
Nuxt.js has a great module that makes Authentication very easy in a Nuxt.js application. It comes with the most commonly used authentication providers out of the box, including, Auth0, Discord, Facebook, Google, and so on.
I, however, recently needed to use a GraphQL-based authentication endpoint in a Nuxt.js application, but there was no out-of-the-box support for this yet. Luckily the Nuxt.js authentication module allows for easy extension/customization. So I implemented a solution myself. And in this post, I will walk you through how I achieved it.
Now, I assume you already know the basics of GraphQL and the basics of the Nuxt.js auth-module. So I'm just going to dive into why you are here.
The goal is to implement a solution that allows us to authenticate a Nuxt.js application using a GraphQL mutation like;
mutation LoginMutation($email: String, $password: String) {
logIn(email: $email, password: $password) {
token
expiresIn
}
}
The challenge, however, is the default Nuxt.js auth schemes use Axios to send auth requests, but unlike traditional endpoints, GraphQL endpoints do not use a library like Axios, they use GraphQL clients such as Apollo Client to send requests.
So, what we are going to do in order to authenticate with a GraphQL endpoint is to override relevant methods in the already existing Local
scheme in the Nuxt.js auth module to allow them to use the apollo client to make authentication requests rather than Axios.
Nuxt.js also has a great module for working with apollo called @nuxtjs/apollo
. And we also need the graphql-tag
npm package to help parse raw GraphQL mutations like the above. I'm assuming you already have the @nuxtjs/auth
module installed and set up in your project. If you don't, this is a good time to install it. In fact, let's install all the packages we need now.
npm install --save @nuxtjs/auth-next @nuxtjs/apollo graphql-tag
# OR
yarn add @nuxtjs/auth-next @nuxtjs/apollo graphql-tag
Then add the modules to your nuxt.config.js
file, and configure your GraphQL endpoint in the apollo
config;
// nuxt.config.js
export default {
// ...
modules: [
// ...
'@nuxtjs/auth-next',
'@nuxtjs/apollo',
],
auth: {
// Options
}
apollo: {
clientConfigs: {
default: {
httpEndpoint: 'http://localhost:4000/graphql', // Your graphql endpiont
}
}
}
}
See the @nuxtjs/apollo documentation for more configuration options.
Also, be sure to activate the Vuex store if you haven’t already, by creating an index.js
file under the store
folder as the auth module will not work without it.

Now once the @nuxtjs/apollo
module is installed and set up, it adds two important properties that we need under the app
object in the Nuxt.js context; apolloProvider
and $apolloHelpers
.
apolloProvider
will be used to access our apollo client so that we can use it to make auth requests inside our custom scheme.And
$apolloHelpers
will be used to set and reset the auth token for the apollo module itself, so that GraphQL requests are authenticated when the user is logged in.
Now we are ready to start building our custom scheme.
First, let's create a new folder in our root directory named schemes
, then create a new file named graphqlScheme.js
inside it.

Next, copy and paste the foundation for our custom scheme into the graqhqlScheme.js
file;
// ~/schemes/graphqlScheme.js
import { gql } from 'graphql-tag'
import { LocalScheme } from '~auth/runtime'
const LOGIN_MUTATION = gql`
mutation LoginMutation($email: String, $password: String) {
logIn(email: $email, password: $password) {
token
expiresIn
}
}
`
export const LOGOUT_MUTATION = gql`
mutation LogOutMutation {
logOut
}
`
export const USER_DETAILS_QUERY = gql`
query UserDetailsQuery {
me {
id
name
email
}
}
`
export default class GraphQLScheme extends LocalScheme {
// TODO: Override relevant LocalScheme methods
}
We first define our GraphQL mutations and user details query, passing them through the qql
function from the graphql-tag
npm package. Finally, we define a class for our custom scheme.
Note: The structure/shape of your mutations and query depends on your back-end implementation. And in this post, we are focusing on the front-end. But if you have any questions about a specific back-end implementation, feel free to drop them in the comment section below and I will do my best to answer them.
Now, let's begin to write out our custom scheme, starting with our login
method;
The login
method accepts two arguments. The first one is the credentials
which holds the username/email and password submitted by the user and the second argument is the auth options, and the only option we are interested in, for now, is the reset
option, which we will set to true
by default.
async login(credentials, { reset = true } = {}) {
// ...
}
Next, we need to access the apolloProvider
and the $apolloHelpers
variables from the Nuxt.js context so we can make the authentication request. We have access to the Nuxt.js context in our scheme as this.$auth.ctx
. Also, apollo can have multiple clients, so we need to specify that we need the defaultClient
, then use ES6 syntax to rename the variable to apolloClient
.
async login(credentials, { reset = true } = {}) {
const {
apolloProvider: { defaultClient: apolloClient },
$apolloHelpers,
} = this.$auth.ctx.app
// ...
}
The next part is to determine if we want to reset leftover tokens before attempting to log in.
async login(credentials, { reset = true } = {}) {
const {
apolloProvider: { defaultClient: apolloClient },
$apolloHelpers,
} = this.$auth.ctx.app
// Ditch any leftover local tokens before attempting to log in
if (reset) {
this.$auth.reset({ resetInterceptor: false })
}
// ...
}
Finally, to finish off the login method, we need to;
Make the login request using
apolloClient
,Update our token saved in the cookie,
And fetch the authenticated user.
The finished login method looks like this;
async login(credentials, { reset = true } = {}) {
const {
apolloProvider: { defaultClient: apolloClient },
$apolloHelpers,
} = this.$auth.ctx.app
// Ditch any leftover local tokens before attempting to log in
if (reset) {
this.$auth.reset({ resetInterceptor: false })
}
// Make login request
const response = await apolloClient
.mutate({
mutation: LOGIN_MUTATION,
variables: credentials,
})
.then(({ data }) => data && data.logIn)
// Update our cookie token
this.token.set(response.token)
// Set our graphql-token so subsequent graphql request are authenticated
await $apolloHelpers.onLogin(response.token)
// Fetch user
await this.fetchUser() // We will define this function next
return response.token
}
Next, let's define our fetchUser
method.
fetchUser() {
const {
apolloProvider: { defaultClient: apolloClient },
} = this.$auth.ctx.app
// Token is required but not available
if (!this.check().valid) {
return
}
// Try to fetch user
return apolloClient
.query({
query: USER_DETAILS_QUERY,
})
.then(({ data }) => {
if (!data.me) {
const error = new Error(`User Data response not resolved`)
return Promise.reject(error)
}
// Set the auth user
this.$auth.setUser(data.me)
return data.me
})
.catch((error) => {
this.$auth.callOnError(error, { method: 'fetchUser' })
return Promise.reject(error)
})
}
This method is pretty straightforward, just like the login method, we first access the apollo defaultClient
, next, we check if we have a valid token set, we then fetch the authenticated user using our USER_DETAILS_QUERY
, and finally, use the auth module's setUser()
method to set the logged-in user.
Next, our logout
method;
async logout() {
const {
apolloProvider: { defaultClient: apolloClient },
$apolloHelpers,
} = this.$auth.ctx.app
await apolloClient
.mutate({
mutation: LOGOUT_MUTATION,
})
.catch(() => {
// Handle errors
})
// Reset regardless
$apolloHelpers.onLogout()
return this.$auth.reset({ resetInterceptor: false })
}
We must call the $apolloHelpers.onLogout()
function after we have logged the user out, to let the @nuxtjs/apollo
module know that the user is no longer authenticated. It is also important to set the resetInterceptor
option to false
when calling the auth module's reset()
method. You only want to set the option to true
if you are using Axios, but we are not using Axios, so setting it to true
will raise errors, especially if we don't have the Axios module installed in our project.
The next function we need to override is initializeRequestInterceptor
. This function is called when the scheme is initialized. What it does is basically initialize Axios interceptors. But since we are not using Axios, we need to override it to avoid raising errors.
initializeRequestInterceptor() {
// Instead of initializing axios interceptors, Do nothing
// Since we are not using axios
}
And finally, let's override the reset
method, which is pretty straightforward as well. We set the logged-in user to false
again using the auth module's setUser()
method, then we reset our token.
reset() {
this.$auth.setUser(false)
this.token.reset()
}
So the finished version of our custom scheme looks like this;
import { gql } from 'graphql-tag'
import { LocalScheme } from '~auth/runtime'
const LOGIN_MUTATION = gql`
mutation LoginMutation($email: String, $password: String) {
logIn(email: $email, password: $password) {
token
expiresIn
}
}
`
export const LOGOUT_MUTATION = gql`
mutation LogOutMutation {
logOut
}
`
export const USER_DETAILS_QUERY = gql`
query UserDetailsQuery {
me {
id
name
email
}
}
`
export default class GraphQLScheme extends LocalScheme {
async login(credentials, { reset = true } = {}) {
const {
apolloProvider: { defaultClient: apolloClient },
$apolloHelpers,
} = this.$auth.ctx.app
// Ditch any leftover local tokens before attempting to log in
if (reset) {
this.$auth.reset({ resetInterceptor: false })
}
// Make login request
const response = await apolloClient
.mutate({
mutation: LOGIN_MUTATION,
variables: credentials,
})
.then(({ data }) => data && data.logIn)
this.token.set(response.token)
// Set your graphql-token
await $apolloHelpers.onLogin(response.token)
// Fetch user
await this.fetchUser()
// Update tokens
return response.token
}
fetchUser() {
const {
apolloProvider: { defaultClient: apolloClient },
} = this.$auth.ctx.app
// Token is required but not available
if (!this.check().valid) {
return
}
// Try to fetch user
return apolloClient
.query({
query: USER_DETAILS_QUERY,
})
.then(({ data }) => {
if (!data.me) {
const error = new Error(`User Data response not resolved`)
return Promise.reject(error)
}
this.$auth.setUser(data.me)
return data.me
})
.catch((error) => {
this.$auth.callOnError(error, { method: 'fetchUser' })
return Promise.reject(error)
})
}
async logout() {
const {
apolloProvider: { defaultClient: apolloClient },
$apolloHelpers,
} = this.$auth.ctx.app
await apolloClient
.mutate({
mutation: LOGOUT_MUTATION,
})
.catch(() => {
// Handle errors
})
// But reset regardless
$apolloHelpers.onLogout()
return this.$auth.reset({ resetInterceptor: false })
}
initializeRequestInterceptor() {
// Instead of initializing axios interceptors, Do nothing
// Since we are not using axios
}
reset() {
this.$auth.setUser(false)
this.token.reset()
}
}
Now that our GraphQL scheme is all done, let's configure it in our nuxt.config.js
file.
// nuxt.config.js
auth: {
strategies: {
graphql: {
scheme: '~/schemes/graphqlScheme.js',
},
},
redirect: {
login: '/login',
logout: '/login?logout=true',
callback: false,
home: '/dashboard',
},
},
And that’s it.
To log in using our new strategy; assuming our email address is [email protected]
and our password is [email protected]
, we simply do;
const credentials = { email: '[email protected]', password: '[email protected]' }
this.$auth.loginWith('graphql', credentials)
To see it in action, let's create a basic login
and a dashboard
pages in our project;

In the login.vue
page, let's create a basic login form using bootstrap-vue components;
<template>
<b-container>
<b-row>
<b-col md="4" offset-md="4" class="mt-5">
<form method="POST" @submit.prevent="handleLoginSubmit">
<div class="form-group">
<label for="email">Email address</label>
<input
id="email"
v-model="form.email"
type="email"
class="form-control"
aria-describedby="emailHelp"
/>
</div>
<div class="form-group">
<label for="password">Password</label>
<input
id="password"
v-model="form.password"
type="password"
class="form-control"
/>
</div>
<button type="submit" class="btn btn-primary" :disabled="formBusy">
<b-spinner v-if="formBusy" small class="mr-2" /> Login
</button>
</form>
</b-col>
</b-row>
</b-container>
</template>
<script>
export default {
middleware: 'auth',
auth: 'guest',
}
</script>
From the above template, you can tell we need to create the form
and formBusy
variables and more importantly the handleLoginSubmit
method. So let's go ahead and do that in the script
section;
<script>
export default {
middleware: 'auth',
auth: 'guest',
data() {
return {
form: {
email: '',
password: '',
},
formBusy: false,
}
},
methods: {
async handleLoginSubmit() {
const credentials = this.form
this.formBusy = true
try {
// Using our custom strategy
await this.$auth.loginWith('graphql', credentials)
this.formBusy = false
} catch (errors) {
this.formBusy = false
// Handle errors
}
},
},
}
</script>
For the dashboard.vue
page, we are going to use the auth module's $auth.user
variable to display the name of the logged-in user in the navbar. We will then implement a logout button.
<template>
<div class="dashboard-page">
<b-navbar toggleable="lg" type="dark" variant="primary">
<b-navbar-brand href="#">Nuxt-graphql-auth</b-navbar-brand>
<b-navbar-toggle target="nav-collapse"></b-navbar-toggle>
<b-collapse id="nav-collapse" is-nav>
<b-navbar-nav class="ml-auto">
<b-nav-item-dropdown right>
<template #button-content>
{{ $auth.user.name }}
</template>
<b-dropdown-item @click="handleLogout">Sign Out</b-dropdown-item>
</b-nav-item-dropdown>
</b-navbar-nav>
</b-collapse>
</b-navbar>
<b-container>
<b-row>
<b-col md="10" offset-md="1" class="mt-5">
<h1>Welcome to the dashboard</h1>
</b-col>
</b-row>
</b-container>
</div>
</template>
<script>
export default {
middleware: 'auth',
methods: {
async handleLogout() {
this.$nuxt.$loading.start()
await this.$auth.logout()
}
},
}
</script>
Now if you navigate to the /login
page and enter the correct email and password combination (according to your GraphQL backend/server), you should be logged in, and redirected to the /dashboard
page.

Backend Implementation
Check out my post on How To Authenticate a Nuxt.js App With a Laravel GraphQL Backend to learn more about the backend implementation.
And that’s it for this post.
You can find the complete source codes used in this tutorial here.
I hope this was helpful. As I said, if you have any questions, drop them in a comment below and I will do my best to answer them. ✌🏽
2 Responses
The article is just what I have been looking for in a while now. Thanks Salem!
Thanks, Rex. I'm glad you found it helpful.