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.
Contributing
See CONTRIBUTING.md.
Latest comments (61)
Hi. Thanks for your posting.
Could you share project that frontend is Nuxt.js and backend is Node.js?
I want to feel with Nuxt and Node through example project.
Thanks.
$auth.getRefreshToken(strategy) always return false to me, what am I doing wrong?
Hi! There are lots of possible causes.
Just a few guesses:
Might it be that vuex/something else that is required for nuxt auth is not enabled in your app?
Or maybe the backend returns false so it gets stored?
Or some other issue, try debugging the setRefreshToken calls, etc.
Hello @mrnaif2018 is there a way i could contact you ? gmail or fb, i need some help with nuxt.js auth.
Hi! I'm @MrNaif_bel on telegram. I might add that to the profile, it would be good if it was supported by dev.to natively.
media.discordapp.net/attachments/4...
I am getting this error when writing the axios.js plugin. What should I do?
As I have written in the article, it is a dynamic environment variable, you can replace it with your setting, or implement environment variables loading as written in my another article:
How to load dynamical environment variables with Nuxt.js
MrNaif2018 ・ Dec 16 '19 ・ 2 min read
Thanks for sharing this. I couldn't help but wonder isn't this solution much simpler? stackoverflow.com/a/53866447/12634779
Hi! It might be, but this solution makes tokens refresh a bit before access token expiration, which is useful sometimes. Plus middleware was needed because of bugs of @nuxt/auth on server side. They are releasing refresh token support soon, so soon all those solutions wouldn't be needed.
Nuxtjs auth module dev community developed a refresh scheme for local strategy and it will be available in @nuxtjs/auth V5, but if you want to use it in the meantime, use the @nuxtjs/auth-next :
dev.auth.nuxtjs.org/schemes/refres...
Awesome! When it will be released I will update the post. I hope my post served it's purpose well for more than 6 months, until it is no longer needed. I am not using JWT tokens anymore, but happy to see they got it built-in finally! No more workarounds! Thank you for telling.
I think its at least, a very dangerous practice to expose an enpoind that returns valid tokens. That kind of strategie dont looks very production ready, as you my bee wanted. You should concider adicional stuff like you might find at owasp.org
Sorry? Please, if you don't like the way it is implemented, suggest you own way. This post is mostly about how to make it working at least, backend structure is not discussed here, just one of possible cases is shown. "Additional" stuff can be anything. Endpoint returning valid token is bad? Then how could application get appropriate tokens to access protected endpoints? Why is it dangerous? There is no other way for code to get tokens without requesting them. Please, if you tell that something is "bad", explain why. Owasp.org has some documents on web applications, but it doesn't tell anything about returning tokens from endpoint.
Hello, thanks for this post, it helps me a lot.
Just a question: I need to pass a param to user url to get the user. I need to do something like that:
so how can I pass userId params to get the user?
Thanks
Hello! You should implement something like /users/me in your backend to just return current user. Unless you JWT token has user id in, it is impossible to fetch that endpoint as it fetches user data(with user id in it).
thanks for the detailed post but can you also add how you would handle facebook google and twitter login with nuxt/auth
This is outside of the scope of this article. It shows how to workaround limitations and bugs about refresh token implementation in nuxt/auth.
Google auth, social providers auth are there:
auth.nuxtjs.org/providers/google.html
auth.nuxtjs.org/providers/facebook...
Etc.
Hi MrNaif
I have a question I really need help with. I thought you would be the one able to help me as you created this amazing plugin.
When trying to access a page with auth middleware, everything works correctly. The problem is, though, that the application asynchronously loads and sees if the token is valid.
This causes 2 redirects that don't look very good for the user experience. How can I make it so that the token gets check SYNCHRONOUSLY and nothing is being shown on the page, and THEN perform redirects whenever the user is not authenticated?
Please see a GIF of the issue here:
i.imgur.com/ZSx5ByF.gif
(PS: Page not found notification is because I haven't defined the login route yet.)
Hello! I think that for the best UX redirects should be done server side, via a middleware. Maybe try adding a middleware which checks token, and redirects based on that. Also those redirects look a little bit like hydration issues, check if you have those.
Thanks for your reply!
Do you mind giving out your contact details? I don't really know what you mean.
There'll be a little reward too ^^
Sure, as long as it is something simple (:
Contact me in telegram(@MrNaif_bel)
how to create @nuxt/auth google can be validated with jwt in my server api
Hmm, sorry? Could you provide more context please? If it is related to google oauth, nuxt auth module has oauth provider built-in.
For anybody here using django-simpleJWT and having trouble with the Refresh token. I'd like to share my solution by modifying lines
and make sure have the following settings in Django
@mrnaif2018 thanks a lot for this tutorial. It saved me a lot of time. @jjspscl Hi, thanks for the update on using django-simpleJWT (that's what my project uses) I've been trying to register a new user but I've been getting a 403 forbidden error on my register endpoint, meanwhile my login works fine. Any idea what could be wrong?
Seems of out topic, it would be best if you'd Chat me
Okay please follow back so I can access you via dm 🙏🏿 thanks
Thanks for sharing that! I was using my custom JWT implementation so responses differ a little bit of course!
Thank you for sharing :)
Dude you have no idea how long I've been trying to solve this as a frontend newbie.
Really appreciate sharing the code <3
Thanks! Together we solved it (:
Thanks for this great post. I implemented it in my application. I have one operation that invalidates the access_token on the server side. I'm trying around with renewing it using the refresh_token, however not really successful for now. Do you have an idea how to implement a check, if the access_token is still valid and if not to refresh it using the refresh_token?
Thank you!
You can see the
~/plugins/auth.js
file and functiondecodeToken
in it.So if token is still valid then you can do like
if (decodeToken(token).exp > 0)
// valid
Basically this piece of code in the plugin does exactly that(also found in
~/plugins/auth.js
file):if (refreshInterval < 0) {
refreshInterval = (await refreshTokenF($auth, $axios, token, refreshToken) * 1000 - Date.now()) * 0.75
$auth.fetchUserOnce()
}