How to make nuxt auth working with JWT - a definitive guide
Tags: node, todayilearned, webdev, vue
Published: 2019-12-07 20:47:13

## Introduction

Nuxt.js is a nice framework for creating both SSR and SPA apps easily in vue. It is easy to use, but sometimes there are some stuff which can block you for weeks.
This stuff for me was adding JWT authentification.

## Backend situation

Let's assume the following situation:
We have a backend, serving a few endpoints:
- `/token` - by sending json in form {"email":"example.com","password":"somepassword"}, if user exists and password is valid, it returns a pair of access token and refresh token
- `/refresh_token` accepting json in form {"token":"refreshtoken"} returning new refresed access and refresh tokens
- `/users/me` - returning current user information, could be anything based on your app.
Any other endpoint is for authorized users only.
Access token duration in my case was 15 minutes, and refresh token duration-7 days(basically the time I want user to be logged in without reentering credentials).

## Frontend setup

Nuxt.js docs recommend using `@nuxtjs/auth` [package](https://auth.nuxtjs.org/).
It supports different auth schemes and stuff, but it doesn't support refresh token out of the  box.
As we have quite a simple API, I picked up local auth scheme.

### Login component

So, in login component, I have the following code:
{% gist https://gist.github.com/MrNaif2018/f36b3cc3ccd162749ee4b026e73d5a50 %}

What does this do? Well, as nuxt auth doesn't support refresh tokens saving with local scheme, to do this with minimal code changes, we do it manually.
We send request to /token endpoint, and if it succeeded, we save the token(in my case I disabled localStorage and left only cookies), save refresh token(local scheme doesn't support this,but the module itself does), and set authorization headers on axios instance(
`this.$auth.ctx.app.$axios.setHeader('Authorization', 'Bearer ' + resp.data.access_token)`
 is redundant, but I just left it to make sure token is set :D)
Next, we fetch current user and manually save it in storage.
That is login scheme.

### Nuxt config

We should do some configuration in nuxt.config.js:
{% gist https://gist.github.com/MrNaif2018/c9ff421f7af1f18c0e0a4a13d8aa3e7f %}

We configure axios baseUrl to some default value, to avoid requests to the server itself and infinite loops(any value is fine, as it will get replaced by actual url in plugin).
Also we enable global `loggedIn` middleware.
Auth module has it's own `auth` middleware, but I'll return to that in a moment.
In auth module settings we disable localStorage(we want some security,right?), and set cookie expire time to 7 days(time when I want the user to get logged out).
Next, we configure our endpoints, it depends on how your backend works, in my case, I have `/token` in post method, no logout endpoint, and `/users/me` endpoint where data is in body(`propertyName: false`).

Next, we add two auth plugins(note, they're specified **NOT** in nuxt plugins, but in **auth module** plugins section.
`~/plugins/axios.js` configures axios baseUrl
and
`~/plugins/auth.js` makes refreshing work.
Note that we enable it client side only, as for some reasons it doesn't work server side(use ssr:false in older versions of nuxt).

### Now, to the plugins!

`~/plugins/axios.js`:
{% gist https://gist.github.com/MrNaif2018/d975d8d32e71d2b6548fa2af2e7aca55 %}

It just configures baseUrl to don't type it everywhere (:
Note, `store.state.env.URL` is dynamically loaded env variable.
Should I write another post about building docker images once, and loading environment variables on server start?
Share your opinions in comments.

`~/plugins/auth.js`:
{% gist https://gist.github.com/MrNaif2018/5261d0084e746a0b8966129779f7b09f %}

Ok, that's a big chunk of code!
Let's investigate what it does!
Strategy constant is local in our case, if you use a different name, change it.
`FALLBACK_INTERVAL` is used when no token is available(i.e. right after login), set it to your token expiry date in miliseconds(so it's 15 minutes or 900 seconds converted to milliseconds).
And multiply that by 0.75, because we want to refresh token a little bit before it's expiry time.

`refreshTokenF` is doing the refresh process.
It sends request to our refresh endpoint, if we have tokens provided.
Then it basically saves tokens to the storage, returning it's parsed expiry time.
If it failed, we log out(it means 7 days passed).

`decodeToken` function is parsing JWT token into it's data.

Now, to real plugin code:
First, we get $auth and $axios plugins from our app instance.
We try getting those tokens from our cookies(plugins are executed on page load), and fallback to our constant interval first.
If we have those tokens in our storage, we parse access token, and get it' s expiry time.
Also, we fetch the user, as when nuxt auth module fetches is, our baseUrl is not yet configured.
If expiry time is less then 0(token expired), we refresh that right away and update expiry time.
Finally, we use setInterval to refresh token at 75% of it's expiry time.

## Middleware

And the final part, middleware.
Why do we need to reinvent the wheel? Because even if logged in, we'll get logged out in production, because server side you're not logged in, so the only difference between default auth middleware and ours is if (!process.client) check, as the middleware should be executed client side only:
{% gist https://gist.github.com/MrNaif2018/4b38609505288e03f6caa9fe2e298cbf %}

# Congratulations!

We did it!
As you can see, nuxt auth module is nice, but unfortunately requires some workarounds. I hope you found this article useful and won't spend weeks like I did trying to fix those strange bugs (:

I did those things while improving my opensource project: BitcartCC.
If you want to contribute to it or just see how I did it, check it out:
{% github https://github.com/bitcartcc/bitcart-admin %}