To the Stars with Quasar & Firebase - Email Authentication
Tags: quasar, firebase, firestore
Published: 2020-02-06 19:00:05

## Table Of Contents
1. [Introduction](#intro)
 - 1.1 [Assumptions](#assumptions)
2. [Firebase Console Setup](#consoleSetup)
3. [Form View](#formView)
 - 3.1 [Forgot Password](#forgotPassword)
4. [Update Firebase Connection File](#firebaseConnection)
5. [Services](#services)
 - 5.1 [Create the Email Service](#emailService)
 - 5.2 [Update the Base Service] (#baseService)
6. [Route Guarding](#routeGuarding)
7. [State/Vuex](#vuex)
 - 7.1 [Auth State](#authState)
 - 7.2 [Auth Actions](#authActions)
8. [Summary](#summary)
9. [Repository](#repo)
10. [About Quasar](#aboutQuasar)

      
### 1. Introduction <a name="intro"></a>

This article builds atop of the initial article, [Initial Service & Structure](https://dev.to/quasar/to-the-stars-with-quasar-firebase-initial-service-structure-1fcf) published in the Quasar-Firebase series. We're going to look at the most common authentication type in most applications: Email.

- 1.1 Assumptions <a name="assumptions"></a>

Before we get started, a few assumptions are in order. In this article, we're going to be breaking down the interactions between Firebase services, the `serverConnection` file, and our `base` service file. We'll be harnessing our store for state management, so the assumption here is that you have a decent grasp of Vuex and its inner workings. The [repository](https://github.com/quasarframework/firebase-sample-apps/tree/master/authentication/email) has the complete code base, so that is always a place for a bit of study if you're a little weak in Vuex, as well as keeping [Vuex's API docs](https://vuex.vuejs.org/api/) close at hand.

Implementing Vuex within the context of Quasar can be initially achieved in one of two ways. The first is durining initial project creation via the Quasar CLI prompt:

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

Or, if you already have a pre-existing application you'll have to set the store up manually. Take a look at the [docs for the setup](https://quasar.dev/quasar-cli/vuex-store#Adding-a-Vuex-Module.).
You'll have to create the directory structure and use the `index.js` that is shown in the docs.

Also, keep in mind Quasar offers a nice set of commands via the CLI. One of them being `new`. You can simply create new modules via the CLI:

```bash
$ quasar new store myModule
```

or

```bash
$ quasar n s myModule
```

Always be sure to update your store's index file to add the new module.

This is also a good spot to point out that we're going to be adding a new context to our application.

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


*Note: This repo already contains a working Firebase API key. In order to set up your own project, you need to replace your    Firebase config values in the `.env` file and replace it with your own key from the [first article](https://dev.to/quasar/to-the-stars-with-quasar-firebase-initial-service-structure-1fcf).*

Be sure to [clone the repo](https://github.com/quasarframework/firebase-sample-apps/) and have the app to follow along with. Navigate to the respective app and run:

```bash
$ npm run dev
```

*A final note, this code is for Vue v2 and Quasar v1.*

### 2. Firebase Console Setup <a name="setup"></a>

To set up email authentication, you first have to enable it in your project in your Firebase console:

- In the Firebase console, click on the *Authentication* menu section.
![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/pmc7ysqu8skcewgb3zsp.png)

- On the Sign-in method tab, enable the Email/Password sign-in method and click Save.
![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/klgde5gfd7ib9g1cfg3z.png)

### 3. Form View <a name="formView"></a>

Now a form for the user to register or login is needed. Create a page called `Auth.vue`. This page houses both registration and login, as well as a link to the, forgot password page.

```bash
$ quasar new page Auth
```





In `Auth.vue.` we've created a basic user form for registering and logging in users.

[**/src/pages/Auth.vue**](https://github.com/quasarframework/firebase-sample-apps/blob/master/authentication/email/src/pages/Auth.vue)

Notice the use of the Vuex convenience method [`mapActions`](https://vuex.vuejs.org/api/#mapactions).

- 3.1 Forgot Password <a name="forgotPassword"></a>

Before we forget, the user needs to reset their password if needed. Using the quasar-cli create a new page:

```bash
$ quasar new page ForgotPassword
```

**[/src/pages/forgotPassword.vue](https://github.com/quasarframework/firebase-sample-apps/blob/master/authentication/email/src/pages/ForgotPassword.vue)**

Update our [routes](https://github.com/quasarframework/firebase-sample-apps/blob/master/authentication/email/src/router/routes.js) file, by adding a new route inside of our `/auth` path:

```js
const routes = [
...

   {
    path: '/auth',
    component: () => import('layouts/MyLayout.vue'),
    children: [
      {
        path: 'forgotPassword',
        name: 'ForgotPassword',
        component: () => import('pages/ForgotPassword.vue')
      },
...
]
```

### 4. Update Firebase Connection File <a name="firebaseConnection"></a>

Now that the form is in place, we need to start to augment a few more files. First, let's take a look at our `firebaseConnection` file:

[**/src/boot/firebaseConnection.js**](https://github.com/quasarframework/firebase-sample-apps/blob/master/authentication/email/src/boot/firebaseConnection.js)

```js
import firebaseServices from '../services/firebase'

export default ({ store, Vue }) => {
  const config = process.env.environments.FIREBASE_CONFIG
  firebaseService.fBInit(config)

  // Tell the application what to do when the 
  // authentication state has changed
  firebaseServices.auth().onAuthStateChanged((user) => {
    firebaseServices.handleOnAuthStateChanged(store, user)
  }, (error) => {
    console.error(error)
  })

  Vue.prototype.$fb = firebaseServices
  store.$fb = firebaseServices
}
```


There are two new statements, one for handling the change of the authentication state, and one for controlling the application's rendering process concerning the route.

Here is where it gets a little tricky, and one of the challenging pieces when dealing with Firebase with Quasar. Just a little tinkering is needed to handle this scenario. The next section will talk specifically about what the mechanics are in our `base` service to halt the rendering of our application while Firebase is initializing.

### 5. Services <a name="services"></a>

A couple of additions are needed now that we're going to be authenticating users. One is a new `email` service, and the other is modifying our `base` service.

  - 5.1 Create the Email Service <a name="emailService"></a>

Take a look at the new service: `email`.

[**/src/services/firebase/email.js**](https://github.com/quasarframework/firebase-sample-apps/blob/master/authentication/email/src/services/firebase/email.js)

This service is very straight forward and provides an interface to the Firebase auth methods themselves. Add the reference of the `email` service into our `firebaseService` object in our `index.js` file. 

[**/src/services/firebase/index.js**](https://github.com/quasarframework/firebase-sample-apps/blob/master/authentication/email/src/services/firebase/index.js)

  - 5.2 Update the Base Service <a name="baseService"></a>

Next, we have some updates on our `base` service. As mentioned at the end of section 4, things are a little tricky when working with Firebase and not having access to when the Vue app is being initialized. Looking back at our `serverConnection` boot file, we can now investigate the two statements in the function. 

When Firebase's authentication state changes, we pass a store reference and the user state over to the method, `handleOnAuthStateChanged`.

```js
/**
 * Handle the auth state of the user and set it in the auth store module.
 * Also sets up redirection if the user loses authentication. The action
 * method will determine where the application routes to.
 * @param  {Object} store - Vuex Store
 * @param  {Object} currentUser - Firebase currentUser
 */
export const handleOnAuthStateChanged = async (store, currentUser) => {
  const initialAuthState = isAuthenticated(store)
  // Save to the store
  store.commit('auth/setAuthState', {
    isAuthenticated: currentUser !== null,
    isReady: true
  })

  // If the user loses authentication route
  // them to the login page
  if (!currentUser && initialAuthState) {
    store.dispatch('auth/routeUserToAuth')
  }
}

```

First, we want to make an initial authentication check regarding the user with our method, `isAuthenticated`. Next, we will set an auth object into our auth store module describing the state of the user and the application.

```js
/**
 * @param  {Object} store - Vuex store
 * @return {Boolean} - isAuthenticated
 */
export const isAuthenticated = (store) => {
  return store.state.auth.isAuthenticated
}
```

Doing this moves the internal state away from the service and in a global location in our store. As an application grows in complexity, relying on state in our store is a cleaner and more consistent way.

A convenience if, in the event a user loses their authentication, we route them immediately to the login screen.

```js
if (!currentUser && initialAuthStateSet) {
  store.dispatch('common/routeUserToAuth')
}
```

*You can test this by going in dev tools and removing the auth token provided by Firebase*
![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/gc3n4r86hkr16gtn74f5.png)


Next, we have our `ensureAuthIsInitialized` method that will be utilized in our route guard coming up in our next section.

```js
/**
 * Async function providing the application time to
 * wait for firebase to initialize and determine if a
 * user is authenticated or not with only a single observable.
 * https://firebase.google.com/docs/reference/js/firebase.auth.Auth#onauthstatechanged
 * @param {Object} store - Vuex store
 * @returns {Promise} - A promise that return firebase.Unsubscribe
 */
export const ensureAuthIsInitialized = async (store) => {
  if (store.state.auth.isReady) return true
  // Create the observer only once on init
  return new Promise((resolve, reject) => {
    // Use a promise to make sure that the router will eventually show the route after the auth is initialized.
    const unsubscribe = firebase.auth().onAuthStateChanged(user => {
      resolve()
      unsubscribe()
    }, () => {
      reject(new Error('Looks like there is a problem with the firebase service. Please try again later'))
    })
  })
}
```

This is what allows us to stay on a `requiresAuth` page and not route us back to login. Also, note the use of declaring the observable as an assigned function, and then later calling it. [`unsubscribe()`](https://firebase.google.com/docs/reference/js/firebase.auth.Auth.html#optional-completed:-firebase.unsubscribe) allows us to resolve the auth service once it's done initializing, and then unsubscribe listening to the observable which is useful when dealing with the navigational guards. If this isn't set up this way, you'll end up with multiple observables in memory every time your user is being routed around your app.

### 6. Route Guarding <a name="routeGuarding"></a>

Here we are guarding our routes.

[**/src/router/index.js**](https://github.com/quasarframework/firebase-sample-apps/blob/master/authentication/email/src/router/index.js)
```js
  /**
    Setup the router to be intercepted on each route.
    This allows the application to halt rendering until
    Firebase is finished with its initialization process,
    and handle the user accordingly
  **/
  Router.beforeEach(async (to, from, next) => {
    const { ensureAuthIsInitialized, isAuthenticated } = firebaseServices
    try {
      // Force the app to wait until Firebase has
      // finished its initialization, and handle the
      // authentication state of the user properly
      await ensureAuthIsInitialized(store)
      if (to.matched.some(record => record.meta.requiresAuth)) {
        if (isAuthenticated(store)) {
          next()
        } else {
          next('/auth/login')
        }
      } else if ((to.path === '/auth/register' && isAuthenticated(store)) ||
        (to.path === '/auth/login' && isAuthenticated(store))) {
        next('/user')
      } else {
        next()
      }
    } catch (err) {
      Notify.create({
        message: `${err}`,
        color: 'negative'
      })
    }
  })
```

The route guard can grow and get more complicated as your needs change. Still, the most important thing here is to halt the application from routing until the Firebase service is done initializing by calling `ensureAuthIsInitialized`. 

Note the check if the user is authenticated and trying to navigate to either the login or registration route. The application will not let the user go to either one of those routes and move them directly to the user page for a better user experience.

### 7 State/Vuex <a name="vuex"></a>
Since Vuex is available from the initial project creation, we place the authenticated state in our store back in the base service file in the `handleOnAuthStateChanged` method. 

  - 7.1 Auth State <a name="authState"></a>
[**/src/store/auth/state.js**](https://github.com/quasarframework/firebase-sample-apps/blob/master/authentication/email/src/store/auth/state.js)

```js
export default {
  isAuthenticated: false,
  isReady: false
}
```

  - 7.2 Auth Actions <a name="authActions"></a>

Earlier we mentioned the use of `mapActions` in our Auth.vue page. Take a look at the methods that can be available via `mapActions`.

[**/src/store/auth/actions.js**](https://github.com/quasarframework/firebase-sample-apps/blob/master/authentication/email/src/store/auth/actions.js)

Also, be sure to take a look at our mutation for our auth store that we are using in our base service during our `handleOnAuthStateChanged`.

[**/src/store/auth/mutations.js**](https://github.com/quasarframework/firebase-sample-apps/blob/master/authentication/email/src/store/auth/mutations.js)



### 8. Summary <a name="summary"></a>

Building from our first [article](https://dev.to/quasar/to-the-stars-with-quasar-firebase-initial-service-structure-1fcf), we now have a starting point in which we can successfully create and log in users against the Firebase SDK, as well as reset their password. After a user is successfully authenticated, they are routed to a protected route that is only accessible once a user is authenticated correctly. We've also augmented our `base` service to allow the Firebase SDK to finish it's initialization process so that we will not route a user back to the login screen if they are already correctly authenticated upon browser refresh.

### 9. Repository <a name="repo"></a>

[Quasar-Firebase Repo: Email Authentication](https://github.com/quasarframework/firebase-sample-apps/tree/master/authentication/email)

Up next: [User Profile](https://dev.to/quasar/to-the-stars-with-quasar-firebase-user-profile-319l)

### 10. About Quasar <a name="aboutQuasar"></a>
Interested in Quasar? Here are some more tips and information:

More info: [https://quasar.dev](https://quasar.dev)
GitHub: [https://github.com/quasarframework/quasar](https://github.com/quasarframework/quasar)
Newsletter: [https://quasar.dev/newsletter](https://quasar.dev/newsletter)
Getting Started: [https://quasar.dev/start](https://quasar.dev/start)
Chat Server: [https://chat.quasar.dev/](https://chat.quasar.dev/)
Forum: [https://forum.quasar.dev/](https://forum.quasar.dev/)
Twitter: [https://twitter.com/quasarframework](https://twitter.com/quasarframework)