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.
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:
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:
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
:
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
:
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:
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:
bitcartcc / bitcart-admin
BitcartCC Admin Panel
BitcartCC Admin Panel
This is the BitcartCC Admin Panel.
It is created to simplify usage of BitcartCC Merchants API, making adding or editing data easy, plus containing a checkout page which can be used by various integrations.
The admin panel always covers 100% of the Merchants API.
Live demo
Contributing
See CONTRIBUTING.md.
所有评论(0)