Authentication best practices for Vue

Authentication in Vue.js

Introduction

Whenever you start to get serious with a project, you will most likely face the issue of how to handle client-side token-based authentication.

You will have to answer these questions:

  • How do I store my user’s token?
  • How do I redirect the user after authentication actions (login/logout)?
  • How do I prevent route access to authenticated and unauthenticated users?

This article will walk you through these questions and try to illustrate with clear code and good practices in mind.

However, keep in mind that all projects have different authenticated behavior. Projects can be just a loading screen until you are logged in (Gmail), or a view of the application without access to every feature (Amazon), so you will probably have to adjust what I will describe here.

Before we start

I have made this repo if you want to run some code.

We use Vuex as the global state library. Vuex is especially suited for auth management since it’s application-scoped. If you don’t want to use Vuex, no worries — we also give some code example without Vuex 🙂

We also use the axios library for the ajax calls.

Finally, this article will not cover how to implement the backend side of the authentication process. We will only focus on the client side.

Login

Let’s start with a simple login form:

When the user has filled the inputs and clicked Login, we execute the login method.

Here is the first important bit of this code snippet.

  • Vuex actions returning promises.

this.$store.dispatch(AUTH_REQUEST, { username, password }).then(...)

Did you know? You can also treat action dispatch as promises. Now we can react to successful logins from the component. Allowing us to redirect accordingly.

The same code without Vuex:

Vuex auth module

Let’s now look at this auth module:

First, let’s initialize the state.

We will have the token field (using local storage stored token if present) and a status field, representing the status of the API call (loading, success or error).

What about using localStorage and not cookies ?

Well they are actually both good solutions, with their own advantages and disadvantages. This post  and this page answer this question pretty well.

Some useful getters:

The ‘isAuthenticated’ getter can seem overkill, however it’s a great way to keep your authentication future proof. By having this getter, you separate data from app logic making it future proof 🙂

Now the action:

And the mutation:

A fairly simple API call from a module. The important bits are:

  • Token state being initialized by its local storage value, if possible.
  • The Authentication request action returns a Promise, useful for redirect when a successful login happens.
  • Good practice: pass the login credentials in the request body, not in the URL. The reason behind it is that servers might log URLs, so you don’t have to worry about credential leaks through logs.

Logout

Since we are at it, let’s implement our logout logic in modules/auth.js:

When clicking on the logout button in one of your components responsible for logout:

In this case, logging out for us means clearing out the user’s token and redirecting them. If you need to perform other Vuex state changes, listen to this AUTH_LOGOUT action and commit.

You can also add a token DELETE request  in your action to delete your user token session when logging out.

Try to keep it simple, as few actions as possible to logout/login.

If you start to create one login/logout action per authentication type that you have, you will have a headache maintaining them.

Use the token

Now that we have managed to retrieve the token and store it, let’s use it!

The following example uses Axios and its default headers.

In modules/auth.js

Now after login, all the Axios calls have the authorization header set to your token. All your API calls are authenticated! And when logging out, we delete the authorization header.

Auto-authentication

Right now if we refresh the app, we do have the state correctly set to the previous token. However, the authorization Axios header isn’t set. Let’s fix it!

In your main.js:

Now your API calls are authenticated when refreshing your app after login!

Authenticated routes

Now you probably want to restrict access to your routes depending on whether they are authenticated or not.

In this case, we want only authenticated users to reach /account.

And unauthenticated users should only be able to reach /login and /.

Here we use navigation guards. They allow us to put conditions on routes access that we use to our advantage in conjunction with the Vuex store.

Note 1: If you do not wish to use Vuex, you can still check for token presence in the local storage rather than looking at the store getters 🙂

Note 2: Ed @posva, maintainer of vue-router, also advises the usage of meta attributes, check it out 🙂

Handling the unauthorized case scenario

…but wait, what if the token is expired? What if the user is unauthorized?

No worries here.

Using Axios, you can intercept all responses, and especially the error response. Just check for all unauthorized responses (HTTP 401) and if so, dispatch a logout action.

In your App.vue

And we are good to go!

Conclusion

What have we achieved here?

  • Isolated authentication logic from the app and other libs.
  • We won’t need to explicitly pass tokens to every API call.
  • Handle all unauthenticated API calls
  • We have auto-authentication
  • We have restricted routes access

What have we learned?

  • Separation of concerns
  • Avoid side effects
  • Action dispatch can return promises

This should give you a pretty solid starting point for handling all of your API calls in your app.

Hopefully, this will be helpful for your future projects! If you’re interested in learning how to avoid cross-site scripting (XSS) in your Vue.js app check out my previous post. And if you’re interested in protecting your apps at runtime, try Sqreen for free.

Subscribe
Notify of
guest
88 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments