Firebase for user authentication in Vue.js
Tags: vue, firebase
Published: 2020-02-05 21:55:47

---
title: Firebase for user authentication in Vue.js
published: true
date: 2020-02-05 19:15:50 UTC
tags: vue, firebase
canonical_url: https://labs.thisdot.co/firebase-for-user-authentication-in-vue-js
---

And the Firebase goes on!

In the first installment of this series, I covered how fast you can kick off your dev life cycle by using the Mirage.js library as a local, and client-side, backend with a REST API in-memory service for your app.

In my second, and most recent, [installment] ([https://dev.to/thisdotmedia/vue-the-mirage-from-this-angle-352p](https://dev.to/thisdotmedia/vue-the-mirage-from-this-angle-352p)), I replaced Mirage.js with Cloud Firestore, a serverless database service offered by Firebase. This, of course, makes our app function in a more realistic manner, giving us an overview of it.

In this article, I will introduce Firebase User Authentication module to authenticate and authorize users accessing the Notes Writer app.

Since my last article, I’ve done a complete face-lift for the Notes Writer app in preparation for user authentication. The app is now more responsive and mobile friendly.

Here is a quick preview of the completed app after the addition of the User Authentication Module.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/062lhqd7hd5iepvr8aj1.png)

The login page, needless to say, is self explanatory.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ep7lhlbr87or4ftx7koe.png)

The home page lists the existing notes, allows deleting or editing those notes and the ability to create new ones. This page introduces the `Logout` button as well.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ke0yz9wylbkhkwuki4y1.png)

The app is shown on a mobile. You review your existing notes by clicking the `burger` button located on the left side of the app header bar.

The source code of this article can be cloned from this GitHub repo: [Notes Writer](https://github.com/bhaidar/notes-writer/tree/add-auth).

## Firebase Authentication

Firebase Authentication Engine utilizes several methods to authenticate a user on your app via means such as Email with a Password, Phone, Facebook, Twitter, Apple ID, and many other options as shown here.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/r2aj6i8wqswn8fjba2l0.png)

For this article, I will be using the **Email/Password** provider to authenticate users on the Notes Writer app.

> You can always consult with the amazing Firebase Authentication docs [here](https://firebase.google.com/docs/auth).

The Firebase Authentication module is also capable of doing user authorization by allowing administrators to define rules on how users can read, write, delete and edit data stored in the Cloud Firestore. You can read more about securing data in the Cloud Firestore [here](https://firebase.google.com/docs/firestore/security/overview).

Let’s move on, and start coding user authentication in our app!

## Demo

We will be adding the authentication feature on top of the new UI branch of the Notes Writer app. Therefore, start by cloning the Git branch by running this command:

`git clone --branch new-ui git@github.com:bhaidar/notes-writer.git`

The command clones the remote `new-ui` branch by creating a local copy of the branch on your computer.

### Install Vue Router

Start by installing the Vue Router NPM package by running this command:

`npm install vue-router`

This command adds the Vue Router library into the app to allow navigation from one route to another.

Locate the **/src/views** folder, and add a new file named `Login.vue`. Initialize an empty Vue component by pasting the following content into the new file:

```
<template>
</template>
<script>
</script>

```

Save the file and switch back to the **router/index.js** file.

### Configure Vue Router and routes

Now that the Vue Router is installed, and the Login view is created, let’s configure the Vue Router in the app, and define the different routes available for the Notes Writer app.

Inside the **router/index.js** file, start by adding the following imports:

```
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from './../views/Home'
import Login from './../views/Login'
const fb = require('./../../firebaseConfig.js')

```

The `firebaseConfig.js` file is imported into this file, and is needed later on to check whether the current user is signed-in already.

Configure the Vue object to use the Vue Router plugin by adding this line of code:

```
Vue.use(VueRouter)

```

This step is required by the Vue engine to install the Vue Router plugin, and make its functionality available to the rest of the app.

Next, we need to create a new instance of the `VuewRouter` object, and define our routes as follows:

```
export const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '*',
      redirect: '/'
    },
    {
      path: '/',
      name: 'home',
      component: Home,
      meta: {
        requiresAuth: true
      }
    },
    {
      path: '/login',
      name: 'login',
      component: Login
    }
  ]
})

```

The `VueRouter` instance defines a catch-all route to redirect users to the Home view. The Home route defines a `meta` object with a single property of `requiresAuth`. This boolean value is used later to decide whether the route requires the user to be signed-in before accessing it.

Finally, the Login route is defined to load the Login view component.

> If you are new to Vue Router plugin I suggest you visit their [docs website](https://router.vuejs.org/)

The Vue Router defines navigation guards and hooks. These guard and hooks are extension points you can implement to change the default behavior of the Vue Router engine when handling a specific route.

In our case, we want to implement the `beforeEach` navigation guard to decide whether the user can access the route he/she intends to visit. The decision is based solely on whether the route at hand requires the user to be authenticated, and that the user is indeed authenticated, and signed-in to the app.

```
router.beforeEach((to, from, next) => {
  const requiresAuth = to.matched.some(x => x.meta.requiresAuth)
  const currentUser = fb.auth.currentUser

  if (requiresAuth && !currentUser) next({ path: '/login', query: { redirect: to.fullPath } })
  else if (!requiresAuth && currentUser) next('/')
  else if (!requiresAuth && !currentUser) next()
  else next()
})

```

This hook or navigation guard accepts a single callback function that defines three main input parameters:

- to: The route you are navigating to.
- from: The route you are coming from.
- next: Is a function that is used to move forward to the next hook in the pipeline, to redirect to a new route, to throw an error, and to terminate the current navigation.

The callback function above started by checking whether the route the user is navigating to requires authentication. It then uses the Firebase Auth API to get a hand on the currently signed-in user.

> You can read more about managing users in Firebase Auth API [here](https://firebase.google.com/docs/auth/web/manage-users).

If the route requires authentication, and the user is not signed-in, then redirect the user to the Login view to enter their credentials, and sign-in to the app before visiting the route.

If the route doesn’t require authentication, and the user is currently signed-in, then redirect the user to the Home view.

If the route doesn’t require authentication, and the user is not signed-in, then let the user continue with the current navigation. For instance, this applies to a user visiting the Login view, or Register view.

Finally, if none of the above applies, the user is allowed to route to the desired route. This applies to the Home view. This route requires authentication, and if the user is currently signed-in, then the user is automatically redirected to the Home view.

To try this navigation guard, run the app by issuing the following command, and try to navigate to the root page `/`:

```
npm run serve

```

You are instantly redirected to the login page to sign-in, and access the app.

The final step is to tell the Vue about the routes that we defined above. Locate, and open the `main.js` file, and make sure to pass over the `routes` object to the root Vue instance as follows:

```
import Vue from 'vue'
import App from './App.vue'
import { store } from './store'
import { router } from './router'
const fb = require('./../firebaseConfig.js')

Vue.config.productionTip = false

let app
fb.auth.onAuthStateChanged(user => {
  if (!app) {
    app = new Vue({
      store,
      router,
      render: h => h(App)
    }).$mount('#app')
  }
})

```

Also notice that I’ve moved the initialization of the Vue root app inside the Firebase `onAuthStateChanged` event handler. This event is usually fired when a change occurs on the currently signed-in user. The user has signed-in, or the user has signed-out. This way, the app won’t initialize before Firebase is fully initialized.

### Implement Login view

Naturally, the next step is to implement the Login view so that users can start accessing the app.

Paste the following inside the `Login.vue` view component:

```
<template>
  <div class="login">
    <section class="session">
      <div class="left"></div>
      <section>
        <header>
          <h1>Notes Writer</h1>
          <p>Welcome to Notes Writer App! Login to your account to manage your notes:</p>
        </header>
        <form
          class="form"
          @submit.prevent
        >
          <div class="form__field">
            <input
              id="email"
              type="text"
              placeholder="Email"
              autocomplete="off"
              v-model.trim="loginForm.email"
            >
            <label for="email">Email:</label>
          </div>
          <div class="form__field">
            <input
              id="password"
              type="password"
              placeholder="Password"
              autocomplete="off"
              v-model.trim="loginForm.password"
            >
            <label for="password">Password:</label>
          </div>
          <button
            @click="login"
            class="btn form__btn--submit"
          >Log In</button>
          <p
            class="errors"
            :style="{visibility: showErrors ? 'visible' : 'hidden'}"
          >Wrong username or password! Try again.</p>
        </form>
      </section>
    </section>
  </div>
</template>

<script>
import { mapMutations } from 'vuex'
const fb = require('./../../firebaseConfig.js')

export default {
  data () {
    return {
      loginForm: {
        email: '',
        password: ''
      },
      errors: null
    }
  },
  computed: {
    showErrors () {
      return this.errors
    }
  },
  methods: {
    ...mapMutations(['setCurrentUser']),
    login: async function () {
      try {
        const user = await fb.auth.signInWithEmailAndPassword(this.loginForm.email, this.loginForm.password)
        this.setCurrentUser(user.user)
        this.$router.push('/')
      } catch (error) {
        this.errors = error
      }
    }
  }
}
</script>

<style lang="scss" scoped>
@import "@/styles/components/login.scss";
</style>

```

The component defines a basic Login form with two fields: Email and Password.

Upon clicking the `Log In` button, the event handler fires:

```
login: async function () {
      try {
        const user = await fb.auth.signInWithEmailAndPassword(this.loginForm.email, this.loginForm.password)
        this.setCurrentUser(user.user)
        this.$router.push('/')
      } catch (error) {
        this.errors = error
      }
    }

```

The `login()` function makes use of the Firebase Auth API to sign-in the user using the `signInWithEmailAndPassword()` function. This function accepts as input both the user’s email and password.

If the request is successful, this function returns the currently signed-in user. The code then calls a Vuex Store mutation to store the current signed-in user. In addition, the user is redirected to the home page to start managing the notes.

In the case of an error, the code catches it, and informs the user that their sign in attempt failed.

The above introduces a new piece of state that we need to manage inside the Vuex Store instance: the `currentUser`. Let’s build it.

Navigate to the **store/index.js** file, and add the following to the `state` object:

```
state: {
    notesList: [],
    note: {},
...
currentUser: {},
  },

```

Also, add the mutation function as:

```
setCurrentUser (state, user) {
state.currentUser = user
},

```

Now the store is ready!

Before you can sign-in using the above Login view, we need to set the Firebase sign-in provider on this app.

Navigate to the Firebase Console.

1. Click on your app.
2. Click the **Authentication** menu item.
3. Select the **Sign-in method** tab.
4. Hover over the first row labeled **Email/Password** and click the pencil icon to enable this option.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/490scutdmokk736z4jex.png)

Once you enable this provider, make sure to hit the `Save` button.

Next, you need to create a new user on the Firebase Console to test the app with.

1. Select the **Users** tab.
2. Click the **Add user** button.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/e8196hvos87hu479r5k7.png)

Enter a valid email, a strong password and hit the **Add user** button to save the new user credentials.

Now that the backend Firebase is ready, let’s run the app, and test the work that has been done so far!

To run the app, issue the following command:

```
npm run serve

```

You enter your newly created user credentials and the app should sign you in to start managing your notes.

### Add Author ID on Note model

Now that you can sign-in to the app, it’s time to introduce the field `Author ID` onto the `Note` model object. Every time you create a new Note, the app will grab the ID of the currently signed-in user and attach it on the Note model object to be stored in the Database.

Every Note should have an author or owner! This change is minor and affects only the `saveNo0te()` action inside the Vuex Store instance. Navigate to the **/store/index.js** file and amend the `saveNote()` action as follows:

```
async saveNote ({ commit, state }) {
      const { id, body, title } = state.note
      const authorId = state.currentUser.uid

      if (id) { // update
        commit('setPerformingUpdate', true)
        await fb.notesCollection.doc(id).update({
          body,
          title,
          updatedOn: fb.firebase.firestore.Timestamp.now()
        })
        commit('setPerformingUpdate', !state.performingUpdate)
      } else { // add
        commit('setPerformingAdd', true)
        await fb.notesCollection.add({
          body,
          title,
          **authorId,**
          createdOn: fb.firebase.firestore.Timestamp.now(),
          updatedOn: fb.firebase.firestore.Timestamp.now()
        })
        commit('setPerformingAdd', !state.performingAdd)
      }
      commit('setNote', {})
    }

```

When creating a new Note record, the code retrieves the currently signed-in User ID and stores this value inside a local variable named `authorId`. This variable is then passed over to the `notesCollection.add()` function when creating a new Note record as I’ve just shown in the source code.

That’s all! Now every Note created in the system has an owner or author. You will see shortly how we are going to use this feature to query for customized and owned Notes only.

### Integrate Authentication into the Vuex Store

The Vuex Store instance should be updated whenever the status of the currently signed-in user changes. To cater for that, we will refactor the code inside the store object as follows:

```
fb.auth.onAuthStateChanged(user => {
  if (user) {
    store.commit('setCurrentUser', user)

    // realtime updates from our notes collection
    fb.notesCollection.orderBy('createdOn', 'desc').onSnapshot(querySnapshot => {
      let notesArray = []

      querySnapshot.forEach(doc => {
        let note = doc.data()
        note.id = doc.id
        notesArray.push(note)
      })

      store.commit('loadNotes', notesArray)
    })
  }
})

```

Now the store watches real-time changes on the `notesCollection` inside the `onAuthStatechanged` event handler callback.

If there is a valid user passed over, the store is updated accordingly. Then the store starts watching any changes on the `notesCollection` object.

### Query for User’s own Notes only

So far the Vuex Store instance is watching the entire `notesCollection` object. However, what’s needed is to query only the notes that belong to the currently signed-in user. To achieve this goal, navigate to the **store/index.js** file, and replace the following line of code:

```
fb.auth.onAuthStateChanged(user => {
  if (user) {
    store.commit('setCurrentUser', user)

    // real-time updates from our notes collection
    **fb.notesCollection.orderBy('createdOn', 'desc').onSnapshot(querySnapshot => {**
      let notesArray = []

      querySnapshot.forEach(doc => {
        let note = doc.data()
        note.id = doc.id
        notesArray.push(note)
      })

      store.commit('loadNotes', notesArray)
    })
  }
})

```

With the following line of code:

```
fb.auth.onAuthStateChanged(user => {
  if (user) {
    store.commit('setCurrentUser', user)

    // real-time updates from our notes collection
    fb.notesCollection.where('authorId', '==', user.uid).orderBy('createdOn', 'desc').onSnapshot(querySnapshot => {
      let notesArray = []

      querySnapshot.forEach(doc => {
        let note = doc.data()
        note.id = doc.id
        notesArray.push(note)
      })

      store.commit('loadNotes', notesArray)
    })
  }
})

```

The code now fetches Note records that belong to the currently signed-in user!

### Configure Authorization rules on Firebase

Previously in this series, I created the [Database](https://dev.to/thisdotmedia/storing-your-notes-in-the-cloud-firestore-with-vuejs-2g1p) and opted for the **Start in test mode**. This mode allows anyone to read and write to the database for 30 days.

Now that we have authentication in place, let’s reassess the Cloud Firestore rules and allow only authenticated users to read, update, create and delete.

Follow the steps below to setup authorization on your Cloud Firestore:

1. Visit the Firebase console and login to your account.
2. Locate and click the **Database** menu item..
3. Click the **Rules** tab.

Replace the content there with:

```
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, create, update, delete: if request.auth.uid != null
    }
  }
}

```

This rule targets any document in the current database and allows any requests that hold an **auth** object with an **uid** that’s not null. Only requests sent by an authenticated user, will carry a valid **auth** object.

That’s it! Pretty easy. Now we can be sure that anonymous requests won’t find their way into our database.

### Add Logout button

Finally, let’s add support for a `Logout` button for the user.

Navigate to the **components/Navbar.vue** component, and insert the following inside the `links` section:

```
<div class="links">
      <a
        href="#"
        title="Logout"
        v-if="showLogout"
        @click="logout"
      ><span class="logout">Logout</span></a>
    </div>

```

Let’s implement the `logout()` function as follows:

```
async logout () {
      await fb.auth.signOut()
      this.clearData()
      this.$router.push('/login')
    },

```

The code calls the `signOut()` function on the Firebase Auth API to sign-out the user, and clear any local cookies or data related to the currently signed-in user.

In addition, it clears the data stored about the currently signed-in user inside the Vuex Store instance.

Finally, it redirects the user to the `Login` page.

Let’s add the `clearData()` action onto the Vuex Store instance. Navigate to the **store/index.js** file, and add the following action:

```
clearData ({ commit }) {
      commit('setCurrentUser', {})
      commit('loadNotes', [])
      commit('setNote', {})
    },

```

The action clears out the signed-in user, all loaded Notes records, and the current Note object.

## Conclusion

We’re done! We’ve implemented user authentication in the Notes Writer app using Firebase Authentication API. The Firebase Authentication is rich in features, and offers more functionalities than we could possibly cover in this article. My suggestion: always refer to the Firebase Authentication docs website to learn more about the different options available.

In the next installment, we’ll make use of Firebase Cloud Functions to both extend the features of the Notes Writer app, and demonstrate the capabilities of the Cloud Functions.

Stay tuned!

_This Dot Inc. is a consulting company which contains two branches : the media stream, and labs stream. This Dot Media is the portion responsible for keeping developers up to date with advancements in the web platform. This Dot Labs provides teams with web platform expertise, using methods such as mentoring and training._