Vue.js Authentication System with Node.js Backend
Tags: vue, node, javascript
Published: 2019-02-08 15:14:44

---
title: Vue.js Authentication System with Node.js Backend
published: true
description: In this tutorial, we'll explore different authentication types for JavaScript applications and build a Vue authentication system with a Node.js backend.
tags: Vue, Node, JavaScript
canonical_url: https://blog.jscrambler.com/vue-js-authentication-system-with-node-js-backend/
cover_image: https://blog.jscrambler.com/content/images/2018/10/jscrambler-blog-vue-js-authentication-system-node-js-backend.jpg
---


**This article was originally published on the [Jscrambler Blog](https://blog.jscrambler.com/vue-js-authentication-system-with-node-js-backend/?utm_source=dev.to&utm_medium=referral) by Lamin Sanneh**.

When building a *Vue.js* authentication system, there are two primary scenarios involved. Scenario one: one party controls both the front-end and back-end; scenario two: a third-party controls the back-end. If it is the latter case, we have to adapt the front-end. It has to deal with whatever authentication type becomes available from the back-end.

The finished code for this tutorial is available at these GitHub repositories:

- [Front-end client](https://github.com/JscramblerBlog/jscrambler-vueauthclient)
- [Back-end client](https://github.com/JscramblerBlog/jscrambler-vueauthclient-backend)


## Front-end JavaScript Authentication Types


In our application, we have the flexibility to choose between various authentication types. This is because we will be in charge of both front-end and back-end. Let us identify the two types. The first one is *local* or *same-domain* authentication — this is the case when the front-end and the back-end are both running on the same domain. The second is *cross-domain* authentication — it is when the front-end and back-end are running on different domains.

 
These are the two main categories but there are many sub-categories under them. In light of the above, we will use *local* authentication since we are in charge of the whole system. We will be using many *Node.js* libraries. But the two main ones are [Passport.js](http://www.passportjs.org/) and [Express.js](https://expressjs.com/). *Passport.js* is an authentication library. It provides several features like *local* authentication, *OAuth authentication* and *Single Sign-On authentication*. *Express.js* is a server framework for *Node.js* used for building web applications.
 
 
## The Application Paradigm
 

Our application front-end will have two main pages: a login page and a dashboard page. Both authenticated and anonymous users will have access to the login page. The dashboard page will only be accessible to authenticated users. The login page will have a form which will submit data through Ajax to our back-end API. Then, the back-end will check if the credentials are correct and reply back to the front-end with a cookie. This cookie is what the front-end will use to gain access to any locked pages.

Revalidation of the cookie happens on every request to a locked page. If the cookie becomes invalid or the user is not logged in, they cannot access the dashboard. The back-end will send an error response and the front-end will know to redirect the user back to the login page.

We will not be setting up a real database — we will use an array of users in the back-end to mimic some form of a database. Finally, we will have a logout link. This will send a request to our server to invalidate our current session and hence log out the current user.

So, let’s begin building our *Vue.js* authentication system using *Node.js* as a back-end.
 

## Vue.js Front-End Setup

 
To begin with, we first need to have the latest version of *Node.js* and *vue-cli* setup. At the time of this article, the latest version of *vue-cli* is version 3. If the installed version is 2, we want to upgrade — we first need to remove the old version by running:

 

```bash
npm uninstall vue-cli -g
```

 

Then install the latest version by running:

 

```bash
npm install -g @vue/cli
```

 

followed by

 

```bash
npm install -g @vue/cli-init
```

 

After setting up the above, go to any folder in the terminal and run:

 

```bash
vue init webpack vueauthclient
```

 

This will create a new application in `vueauthclient` using the [webpack](https://webpack.js.org/) folder organization.

We should get some prompts on the command line. It is safe to select all the defaults — but for this article, we can select “no” for the tests. Next, navigate to this folder using `cd vueauthclient` and run the application using:

 

```bash
npm run dev
```

 

This will launch a development server which is accessible at the URL `localhost:8080`. After visiting this URL,  the Vue.js logo with some text should be visible on the page. The `Vue.js` component responsible for displaying this page lives in the file:

```javascript
vueauthclient/src/components/HelloWorld.vue
```


## Main Login Screen

 
Let us set up our login page. Then, we will change the homepage to default to the login page screen which we are yet to create. From now on, we will leave out the main application folder `vueauthclient`, when referring to files.

 

Let us install the Ajax library called [Axios](https://github.com/axios/axios) using:

 

```bash
npm install axios --save
```

 

This is a library which makes it easier to do HTTP Ajax calls to any back-end server. It is available for both front-end and back-end applications but here, we will only use it on the front-end.

Next, create a login component file in `src/components/Login.vue`. In this file, paste the following:

 

```html
<template>
    <div>    
        <h2>Login</h2>    
        <form v-on:submit="login">    
            <input type="text" name="email" /><br>    
            <input type="password" name="password" /><br>    
            <input type="submit" value="Login" />    
        </form>    
    </div>
</template>

<script>
    import router from "../router"    
    import axios from "axios"    
    export default {    
        name: "Login",    
        methods: {    
            login: (e) => {    
                e.preventDefault()    
                let email = "user@email.com"   
                let password = "password"    
                let login = () => {    
                    let data = {    
                        email: email,    
                        password: password    
                    }    
                    axios.post("/api/login", data)    
                        .then((response) => {    
                            console.log("Logged in")    
                            router.push("/dashboard")    
                        })    
                        .catch((errors) => {    
                            console.log("Cannot log in")    
                        })    
                }    
                login()    
            }    
        }    
    }
</script>
```

Let's break down this code to see what is happening.

 
The template part below is a form with two input fields: `email` and `password`. The form has a `submit` event handler attached to it. Using the *Vue.js* syntax `v-on:submit="login"`, this will submit the field data to the `login` component method.

 

```html
<template>
    <div>
        <h2>Login</h2>
        <form v-on:submit="login">
            <input type="text" name="email" /><br>
            <input type="password" name="password" /><br>    
            <input type="submit" value="Login" />    
        </form>    
    </div>
</template>
```

 

In the script part of the code, as shown below, we are importing our router file. This lives in `src/router/index.js`. We are also importing the *Axios* ajax library for the front-end. Then, we are storing the user credentials and making a login request to our back-end server:

 

```html
<script>
    import router from "../router"        
    import axios from "axios"    
    export default {    
        name: "Login",    
        methods: {    
            login: (e) => {    
                e.preventDefault()   
                let email = "user@email.com"
                let password = "password"
                let login = () => {
                    let data = {
                        email: email,
                        password: password
                    }
                    axios.post("/api/login", data)
                        .then((response) => {
                            console.log("Logged in")
                            router.push("/dashboard")
                        })
                        .catch((errors) => {
                            console.log("Cannot login")
                        })
                }
                login()
            }
        }
    }
</script>
```

 
In the script area below,
 

```javascript
e.preventDefault()
let email = "[user@email.com](mailto:user@email.com)"
let password = "password"
```

 

We are storing hard-coded username and password in variables for now. This helps speed up development by preventing us from retyping the same thing. Later, we will switch those out and get the real data from the form submission.

 
In the final part of the code below, we are making an ajax call using the credentials above. In the case of an `ok` response from the server, we redirect the user to the *dashboard*. If the response is not `ok`, we stay on the same page and log an error in the console.

 

```javascript
let login = () => {
  let data = {
    email: email,
    password: password
  }
  axios.post("/api/login", data)
    .then(response => {
      console.log("Logged in")
      router.push("/dashboard")
    })
    .catch(errors => {
      console.log("Cannot login")
    })
}
login()
```

 

Now that we have our login component set up, let’s change the router to make sure it recognizes the new page. In the file `src/router/index.js`, change the existing router to this:

 

```javascript
import Vue from "vue"
import Router from "vue-router"
import Login from "@/components/Login"
import HelloWorld from "@/components/HelloWorld"
Vue.use(Router)
export default new Router({
  routes: [
    {
      path: "/",
      name: "HelloWorld",
      component: HelloWorld
    },
    {
      path: "/login",
      name: "Login",
      component: Login
    }
  ]
})
```

 

What we’ve done is import our new component, then add an object to the routes array. Remove the HelloWorld route registration, as we won't be needing it anymore.


Finally, for the login, page, let's make sure it is the default page of our application. Change the current path of the login route registration from

 

```javascript
path: "/login",
```

 

to

 

```javascript
path: "/",
```

 

Don’t forget to delete the route registration for the HelloWorld route or else an error might occur. Navigating to `localhost:8080` again in the browser, we should see our new login form. Submitting it at this stage will not do anything except complain that the back-end URL `localhost:8080/api/login` does not exist.

 

## Setup First Secure Page - The Dashboard

 

Now onto the dashboard page. Create a component for it by making a file at `src/components/Dashboard.vue`. In there, paste the following:

 

```html
<template>
    <div>    
        <h2>Dashboard</h2>    
        <p>Name: {{ user.name }}</p>    
    </div>
</template>
<script>
    import axios from "axios"    
    import router from "../router"    
    export default {    
        name: "Login",    
        data() {    
            return {    
                user: {    
                    name: “Jesse”    
                }    
            }    
        },    
        methods: {    
            getUserData: function() {    
                let self = this    
                axios.get("/api/user")    
                    .then((response) => {    
                        console.log(response)    
                        self.$set(this, "user", response.data.user)    
                    })    
                    .catch((errors) => {    
                        console.log(errors)    
                        router.push("/")    
                    })    
            }    
        },    
        mounted() {    
            this.getUserData()    
        }    
    }
</script>
```

 

In the template section, we are displaying the current username. Before setting up the back-end, we will hardcode a user in the front-end. This is so that we can work with this page or else we will get an error.

 
In the script section, we are importing *Axios* library and our router. Then, we have a data function for our component where we return an object with a user property. As we can see, we currently have some hardcoded user data.

We also have two methods called `getUserData` and `mounted`. The *Vue.js* engine calls the `mounted` method when the component has opened. We only have to declare it. The second method, `getUserData` is called in the `mounted` method. In there, we are making a call to the back-end server to fetch the data for the currently logged in user.

 
During the call to the back-end, we get a response from the server. We will have to handle two possible scenarios depending on the response type.
First, if the call was successful, we set the user property with the data returned from the back-end using:

 

```javascript
self.$set(this, "user", response.data.user)
```

 

Secondly, if there was a login issue, the server responds with an error. Then, the front-end redirects the user back to the login page with this line:

 

```javascript
router.push("/")
```

 

We use the `push` method above for redirection and it is available in the package called `vue-router`, the default router for *Vue.js*. Let’s add in the route config for this page by adding this to the route file, like we did for the login page. Import the component:

```javascript
import Dashboard from "@/components/Dashboard"
```

 

And add the route definition:

 

```javascript
{
    path: "/dashboard",
    name: "Dashboard",
    component: Dashboard
}
```

 

## Setup Front-End Data Layer with Axios

 

Now that we have our front-end pages in place, let’s configure *Axios* and *Vue.js*. We will make them ready to communicate with our back-end. Because we are in the development phase, the front-end is running on port `8080`. Once we start developing our back-end server, it will be running on a different port number 3000. This will be the case until we are ready for production.

There is nothing stopping us from running them on the same port. In fact, will eventually be the case in the end. If we recollect, we are going for the *same-domain* approach. We will run the back-end and front-end on different ports for now. This is because we want to take advantage of the many useful features of the *Vue.js* development server. We will touch on how to merge the two (front and back-end) in a later chapter.

 
Before moving on, let's highlight one issue here. There is a drawback to developing our application on different ports. It is called [Cross-Origin Request Sharing](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS), shortly named `CORS`. By default, it will not allow us to make *cross-domain* Ajax requests to our back-end. There is a *Node.js* library to find a way around that but we will leave that for another tutorial.

<a href="https://blog.jscrambler.com/how-to-protect-your-vue-js-application-with-jscrambler/?utm_source=dev.to&utm_medium=referral" target="_blank"><img alt="Protect your Vue App with Jscrambler" src="https://media.jscrambler.com/blog/vue-ad.jpg"></a>

The *Vue.js* development server has something called *proxying*. It allows our back-end server to think that the front-end is running on the same port as itself. To enable that feature, open up the config file in `config/index.js`. Under the `dev` property, add in an object like so:

```javascript
proxyTable: {

"/api": "http://localhost:3000"

},
```

In the above code, we are rerouting Ajax requests that begin with */api* to the URL `http://localhost:3000`. Notice that this is different from the URL our front-end application is running on. If we did not have this code, the Ajax requests by default are sent to `http://localhost:8080`, which is not what we want. When ready for production, we can remove it:

Finally, install the front-end cookie library using:

```bash
npm install vue-cookies --save
```

## Securing our Back-End API

Let’s now move onto setting up a *Node.js* back-end. First of all, we need to have [Node.js](https://nodejs.org/en/) installed on your system for this part as well. Head over to a terminal window. Create an empty folder called `vueauthclient-backend`. Navigate to the folder using:

```bash
cd vueauthclient-backend
```

Then initialize a new *Node.js* application using the command:

```bash
npm init
```

There will be several prompts. Let's accept the defaults and specify values where required. We should end up with a file called `package.json`. Create a file called `index.js` in the project's root directory. This is where our main code will live. Install several libraries using the command: 

```bash
npm install --save body-parser cookie-session express passport passport-local
```

- the *body-parser* library allows us to access values from an Ajax request sent from a front-end.
- *cookie-session* allows us to store cookies on the server and to be able to send one back to a client when they log in.
- *express* is our `Node.js` framework which helps us build Ajax APIs. It also allows us to serve static files from our `Node.js` application.
- *passport.js* is a library to help us authenticate users. It does this by creating sessions and managing them for each user.
- *passport-local* is a library component for *Passport.js*. It specializes in simple authentication by using the *local* authentication type. For example, if we want to use `SSO` login type, we will need to install the component of *Passport.js* that has that feature. So now that we have our libraries installed, let’s import and set them up. 

At the top of the `index.js` file, import the libraries using the code: 

```javascript
const express = require('express')
 
// creating an express instance
const app = express()
const cookieSession = require('cookie-session')
const bodyParser = require('body-parser')
const passport = require('passport')

// getting the local authentication type
const LocalStrategy = require('passport-local').Strategy
``` 

First, let’s initialize the *cookie-session* and the *body-parser* libraries using: 

```javascript
app.use(bodyParser.json())

app.use(cookieSession({
    name: 'mysession',
    keys: ['vueauthrandomkey'],
    maxAge: 24 * 60 * 60 * 1000 // 24 hours
}))
```

We are setting the cookie to expire after 24 hours. Next, let’s instruct our *Node.js* app that we want to use *Passport.js*. Do that by adding the line: 

```javascript
app.use(passport.initialize());
``` 

Next, tell *Passport.js* to start its session management system: 

```javascript
app.use(passport.session());
``` 

Since we won’t be using a real database for managing users, for brevity’s sake we will use an array for that. Add in the following lines: 

```javascript
let users = [
  {
    id: 1,
    name: "Jude",
    email: "user@email.com",
    password: "password"
  },
  {
    id: 2,
    name: "Emma",
    email: "emma@email.com",
    password: "password2"
  }
]
``` 

Next, let’s set up the URLs for logging in, logging out and getting user data. These will be found at `POST /api/login`, `GET /api/logout` and `GET /api/user`, respectively. For the login part, paste in the following:

```javascript
app.post("/api/login", (req, res, next) => {
  passport.authenticate("local", (err, user, info) => {
    if (err) {
      return next(err);
    }

    if (!user) {
      return res.status(400).send([user, "Cannot log in", info]);
    }

    req.login(user, err => {
      res.send("Logged in");
    });
  })(req, res, next);
});
```

In here, we are instructing *Express.js* to authenticate the user using the supplied credentials. If an error occurs or if it fails, we return an error message to the front-end. If the user gets logged in, we will respond with a success message. *Passport.js* handles the checking of credentials. We will set that up shortly. Note that the method `passport.authenticate` resides in the *Passport.js* library. 

The next URL we will set up is `logout`. This invalidates our *cookie* if one exists. Add this to achieve the functionality: 

```javascript
app.get("/api/logout", function(req, res) {
  req.logout();

  console.log("logged out")

  return res.send();
});
``` 

Finally, the URL to get the currently logged in users’ data. When logged in, `Passport.js` adds a user object to the request using the cookie from the front-end as an identifier. We have to use the id from that object to get the required user data from our array of data in the back-end. Paste in the following:

```javascript
app.get("/api/user", authMiddleware, (req, res) => {
  let user = users.find(user => {
    return user.id === req.session.passport.user
  })

  console.log([user, req.session])

  res.send({ user: user })
})
```

Notice that, this time, we have a second variable we are passing in before the callback. This is because we want to protect this URL, so we are passing a middleware filter. This filter will check if the current session is valid before allowing the user to proceed with the rest of the operation. Let’s create the middleware using: 

```javascript
const authMiddleware = (req, res, next) => {
  if (!req.isAuthenticated()) {
    res.status(401).send('You are not authenticated')
  } else {
    return next()
  }
}
``` 

We have to make sure to declare it before creating the API route for `/api/user`. 

Next, let’s configure *Passport.js* so it knows how to log us in.  After logging in, it will store the user object data in a *cookie-session*, and retrieve the data on later requests. To configure *Passport.js* using the *local strategy*, add in the following: 

```javascript
passport.use(
  new LocalStrategy(
    {
      usernameField: "email",
      passwordField: "password"
    },

    (username, password, done) => {
      let user = users.find((user) => {
        return user.email === username && user.password === password
      })

      if (user) {
        done(null, user)
      } else {
        done(null, false, { message: 'Incorrect username or password'})
      }
    }
  )
)
``` 

In here, we are instructing *Passport.js* to use the `LocalStrategy` we created above. We are also specifying which fields to expect from the front-end as it needs a username and password. Then, we are using those values to query the user. If these are valid, we call the `done` callback, which will store the user object in the session. If it is not valid, we will call the done callback with a `false` value and return with an error. One thing to note is that the above code works in conjunction with the `login` URL.  The call to `passport.authenticate` in that URL callback triggers the above code.

Next, let’s tell *Passport.js* how to handle a given user object. This is necessary if we want to do some work before storing it in session. In this case, we only want to store the id as it is enough to identify the user when we extract it from the cookie. Add in the following to achieve that: 

```javascript
passport.serializeUser((user, done) => {
  done(null, user.id)
})
``` 

Next, let’s set up the reverse. When a user makes a request for a secured URL. We tell passport how to retrieve the user object from our array of users. It will use the id we stored using the `serializeUser` method to achieve this. Add this: 

```javascript
passport.deserializeUser((id, done) => {
  let user = users.find((user) => {
    return user.id === id
  })

  done(null, user)
})
``` 

Now, let’s add the code which boots up the *Node.js* server using the following: 

```javascript
app.listen(3000, () => {
  console.log("Example app listening on port 3000")
})
``` 

Run the command: 

```javascript
node index.js
```

This actually starts the server. There will be a message in the console with the text *Example app listening on port 3000*.

## Getting Ready for Production

Now, when we visit the page `localhost:8080`, we should see a login form. When we submit the form, we get redirected to the dashboard page. We achieve this using the proxy we set up earlier. 

This is acceptable for development — but it defeats the purpose of having a *same-domain* application. To have a *same-domain* scenario, we need to compile our application for production. 

Before that, let's test that the proxy is working. Comment out the proxy URL code in `config/index.js`. We may need to restart the development server because we changed a config file.
 
Now, let's revisit the login page and submit the form. We will get an error saying that we aren’t allowed access to the back-end server. To get around this, we need to configure our *Node.js* back-end server. The back-end will now serve our front-end application instead of the development server.

In the console for the front-end, run the command: 

```bash
npm run build
```

This will generate all the necessary files needed for production. We can find all the created files from this command in the `dist` folder. From this point onwards, we have two options: we can either copy this folder over so it is part of our *Node.js* application or we can tell the *Node.js* server to refer directly to it on our file system. The latter is useful if we still want them as separate repositories. We will use the latter method.

Navigate to the folder `dist`. Run the command `pwd` to get the absolute path of the dist folder, assuming we are on a Linux based system or Mac. If we are on Windows, we can get the absolute path to the folder using an equivalent command.

Copy the absolute path but do not forget to restart the *Node.js* server after any modification. Since we do not want to keep restarting the server, let's install [nodemon](https://github.com/remy/nodemon). It can handle that for us when our code changes. 

Next, paste in the following after the import statements:

```javascript
const publicRoot = '/absolute/path/to/dist'

app.use(express.static(publicRoot))
```

This is telling the server where to look for files.

The final step will be to add a route to the root of our *Node.js* application. This is so it serves the *production-ready* code we had compiled. Do that by adding: 

```javascript
app.get("/", (req, res, next) => {
  res.sendFile("index.html", { root: publicRoot })
})
``` 

Now, even with the proxy disabled, let's visit the server root  `localhost:3000`. We will see the login form. Submit this and we should see the dashboard page with the username displayed. 

## Logout Functionality and Login Data 

Note that our application is still using hardcoded data, we want to get that from the submitted form. Change these lines in the login component from:
 
```javascript
let email = "user@email.com"

let password = "password"
``` 

to:

```javascript
let email = e.target.elements.email.value

let password = e.target.elements.password.value
``` 

Now, we are using the data from the form. Next, let’s set up a link to log us out. In the component `src/App.vue`, change the template to this: 

```html
<template>
    <div id="app">    
        <img src="./assets/logo.png">    
        <div>    
            <router-link :to="{ name: 'Dashboard'}">Dashboard</router-link>    
            <router-link :to="{ name: 'Login'}">Login</router-link>    
            <a href="#" v-on:click="logout">Logout</a>    
        </div>    
        <router-view/>    
    </div>
</template>
```

Here, we have created links to the login page, the dashboard page, and a logout link. The logout link does not have a corresponding method currently, so let’s create that. In `src/App.vue` add a logout method in the `scripts` section:

```javascript

logout: function (e) {
    axios
      .get("/api/logout")
      .then(() => {
        router.push("/")
      })
}
```

Here, we are making an Ajax request to the *Node.js* back-end. Then, we redirect the user to the login page when the response has returned. The logout will not work with our deployed app because we need to redeploy it again for production using: 

```bash
npm run build
```

Now, we can revisit the URL `localhost:3000`. We can log in, log out and visit the dashboard page.

## Conclusion

After this tutorial, we should be able to add as many authenticated pages as we want.

If this is the first time using *Vue.js*, please refer to our introductory [blog post here](https://blog.jscrambler.com/build-app-vue-js/?utm_source=dev.to&utm_medium=referral). It will help in setting up and building a basic *Vue.js* application.

Also, don’t forget to protect you Vue.js application from code theft and reverse engineering. See <a href="https://blog.jscrambler.com/how-to-protect-your-vue-js-application-with-jscrambler/?utm_source=dev.to&utm_medium=referral" target="_blank">our handy guide</a> on protecting Vue.js apps with Jscrambler.