E-Mail/Password Authentication with Firebase and Nuxt
Tags: javascript, vue, nuxt, firebase
Published: 2019-10-19 14:08:46

You want to add Firebase to your Nuxt.js project? Here is how you can do that!

## Environment

- node 12.10.0
- npm 6.11.3
- nuxt 2.10.0
- firebase (npm package) 7.2.1

> The code snippets provided in this article are following the Prettier style guidelines.

## Part Firebase

### Create a project and set auth up

First, go to the [Firebase Console](https://console.firebase.google.com/) and add a new project. Afterwards click on the *Authentication*  link and manage the *Sign-In Methods*. You can choose between E-Mail/Password and various social network vendors.

Don't forget to whitelist the URLs domains on the same page. Usually you will need `localhost` for running your Nuxt.js application in development and your custom domain for production.

You're already done with the setup!

### Firebase SDK snippet

Either, head back to your project overview and select the web application option or go to *Settings* > *General* to copy the Firebase SDK snippet. You will need two things:

1. The core Firebase JS SDK
1. Your Web app's Firebase configuration snippet

## Part Nuxt.js


### Core SDK

The URL for the core Firebase JS SDK should be loaded upfront, so you should put it into the `head` block. To do that in your Nuxt.js project, open the `nuxt.config.js` and add the script to the `head` object:

```js
  head: {
    script: [
      {
        src: 'https://www.gstatic.com/firebasejs/[VERSION-NUMBER]/firebase-app.js'
      }
    ]
  },
```


### Plugin

To add the Firebase configuration to your Nuxt.js project, you have to install the npm package `firebase` and create a new plugin.

```shell
  # Using npm
  npm install firebase --save

  # Using yarn
  yarn add firebase
```

Create a new file `firebase.js` (or any other name)  in the `~/plugins` directory and add your Firebase configuration:

```js
// ~/plugins/firebase.js

import firebase from 'firebase'

const firebaseConfig = {
  apiKey: 'your-api-key',
  authDomain: 'project-id.firebaseapp.com',
  databaseURL: 'https://project-id.firebaseio.com',
  projectId: 'project-id',
  storageBucket: 'project-id.appspot.com',
  messagingSenderId: 'messaging-sender-id',
  appId: 'app-id'
}

const app = firebase.initializeApp(firebaseConfig)

export const db = app.database()
```

> If you get the error `Firebase: Firebase App named '[DEFAULT]' already exists (app/duplicate-app).` you are initializing several instances of the Firebase App and should check if there is one before initializing a new one

The following code improves the plugin by checking if there is already an application initialized:

```js
// ~/plugins/firebase.js

import firebase from 'firebase'

let app = null;

const firebaseConfig = {
  apiKey: 'your-api-key',
  authDomain: 'project-id.firebaseapp.com',
  databaseURL: 'https://project-id.firebaseio.com',
  projectId: 'project-id',
  storageBucket: 'project-id.appspot.com',
  messagingSenderId: 'messaging-sender-id',
  appId: 'app-id'
}

app = firebase.apps.length
  ? firebase.app()
  : firebase.initializeApp(firebaseConfig)

export const db = app.database()
```

> If you're coding in TypeScript, don't forget to change the first line of the code snippet above to `import * as firebase from 'firebase'`.

Don't forget to register that plugin in your `nuxt.config.js`:

```js
plugins: ['~/plugins/firebase.js'],
```

That's it! The bridge between Firebase and Nuxt.js is done.


### Authentication Example

To use the Authentication open your Vue SFC (Single File Component), located either in the directory `~/pages` or `~/components` depending on your project's structure.

In this file, first, you will need to import firebase in the `<script>` block again:

```js
// <script> block
import firebase from 'firebase'

// or when writing TypeScript
import * as firebase from 'firebase'
```

Now you have access to `firebase` in this component. The API for authentication is accessible using the method `auth()`. To check the current authentication state during the user's visit on the page, you can use `onAuthStateChanged` in the `created` lifecycle hook. The method returns a user object. It includes amongst other things the email address of the user.


```js
// <script> block
asyncData() {
  return {
    authenticatedUser: null
  }
},
created() {
  firebase.auth().onAuthStateChanged(user => (this.authenticatedUser = user))
}
```

First, you will need to give the user the possibility to do a registration. Assuming the user should enter the password twice during registration the script looks like this:

```js
// <script> block
asyncData() {
  return {
    email: '',
    password: '',
    registrationPassword: ''
  }
},
methods: {
  register() {
    if (this.password === this.registrationPassword) {
      firebase
        .auth()
        .createUserWithEmailAndPassword(this.email, this.password)
    } else {
      // display error message
    }
  }
}
```

Now you have to add the form in the `<template>` block of your Vue SFC.

```html
<!-- <template> block -->
<form @submit.prevent="register">
  <input type="email" v-model="email" placeholder="Your email address" />
  <input type="password" v-model="password" placeholder="Your password" />
  <input type="password" v-model="registrationPassword" placeholder="Re-enter password" />
  <button>Register</button>
</form>
```

> Currently Firebase only accepts passwords with a minimum length of 6 characters.

The user is now successfully registrated. You can check the existance of the database entry in the Firebase console by clicking on *Authentication* > *Users*.

To provide some feedback for the user, you could show a message with the email address of the authenticated user. Also, you can present a logout button:

```html
<!-- <template> block -->
<div v-if="authenticatedUser">
  <p>You are logged in as {{ authenticatedUser.email }}.</p>
  <p>Logout?</p>
  <button @click.prevent="logout">Logout</button>
</div>
```


```js
// <script> block
methods: {
  logout() {
    firebase.auth().signOut()
  }
}
```

For login and logout you can call `signInWithEmailAndPassword` with the email and the password.

```html
<!-- <template> block -->
<form @submit.prevent="login">
  <input type="email" v-model="email" placeholder="Your email address" />
  <input type="password" v-model="password" placeholder="Your password" />
  <button>Login</button>
</form>
```

```js
// <script> block
methods: {
  login() {
      firebase.auth().signInWithEmailAndPassword(this.email, this.password)
  }
}
```

Here is the full example of a simple Register-Login-Logout component:

```html
<template>
  <div>
    <div v-if="authenticatedUser">
      <p>You are logged in as {{ authenticatedUser.email }}.</p>
      <p>Logout?</p>
      <button @click.prevent="logout">Logout</button>
    </div>
    <div v-else>
      <input type="radio" id="hasAccount" :value="false" v-model="needsAccount" />
      <label for="hasAccount">I have an account</label>
      <br />
      <input type="radio" id="needsAcctouns" :value="true" v-model="needsAccount" />
      <label for="needsAcctouns">I need an account</label>
      <form @submit.prevent="loginOrRegister">
        <input type="email" v-model="email" placeholder="Your email address" />
        <input type="password" v-model="registrationPassword" placeholder="Your password" />
        <input
          v-if="needsAccount"
          type="password"
          v-model="registrationPassword"
          placeholder="Re-enter Password"
        />
        <button v-text="needsAccount ? 'Register' : 'Login'" />
      </form>
    </div>
  </div>
</template>
```
```js
<script>
import firebase from 'firebase'

export default {
  name: 'Login',
  asyncData() {
    return {
      authenticatedUser: null,
      email: '',
      password: '',
      registrationPassword: '',
      needsAccount: true
    }
  },
  methods: {
    register() {
      if (this.password === this.registrationPassword) {
        firebase
          .auth()
          .createUserWithEmailAndPassword(this.email, this.password)
      } else {
        // display error message
      }
    },
    login() {
      firebase.auth().signInWithEmailAndPassword(this.email, this.password)
    },
    loginOrRegister() {
      if (this.needsAccount) {
        this.register()
      } else {
        this.login()
      }
    },
    logout() {
      firebase.auth().signOut()
    }
  },
  created() {
    firebase.auth().onAuthStateChanged(user => (this.authenticatedUser = user))
  }
}
</script>
```

## That's it, you're done! 🎉

## Next Steps

Next, you can add some validation and other cases. For example, if you try to register an email address that is already in the database you will get the error: `uncaught exception: Error: The email address is already in use by another account`. Catch this exception and provide a message for the user.

1. Case already registered
1. Case forgot/reset password (with `firebase.auth.sendPasswordResetEmail`)
1. Add Authentication via social media vendors (e.g. `firebase.auth.GoogleAuthProvider()`
1. Split the component into multiple components.