E-Mail/Password Authentication with Firebase and Nuxt

vannsl

Vannsl

Posted on October 19, 2019

E-Mail/Password Authentication with Firebase and Nuxt

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 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
  2. 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:

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

Plugin

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

  # Using npm
  npm install firebase --save

  # Using yarn
  yarn add firebase
Enter fullscreen mode Exit fullscreen mode

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

// ~/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()
Enter fullscreen mode Exit fullscreen mode

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:

// ~/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()
Enter fullscreen mode Exit fullscreen mode

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:

plugins: ['~/plugins/firebase.js'],
Enter fullscreen mode Exit fullscreen mode

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:

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

// or when writing TypeScript
import * as firebase from 'firebase'
Enter fullscreen mode Exit fullscreen mode

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.

// <script> block
asyncData() {
  return {
    authenticatedUser: null
  }
},
created() {
  firebase.auth().onAuthStateChanged(user => (this.authenticatedUser = user))
}
Enter fullscreen mode Exit fullscreen mode

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:

// <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
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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

<!-- <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>
Enter fullscreen mode Exit fullscreen mode

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:

<!-- <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>
Enter fullscreen mode Exit fullscreen mode
// <script> block
methods: {
  logout() {
    firebase.auth().signOut()
  }
}
Enter fullscreen mode Exit fullscreen mode

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

<!-- <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>
Enter fullscreen mode Exit fullscreen mode
// <script> block
methods: {
  login() {
      firebase.auth().signInWithEmailAndPassword(this.email, this.password)
  }
}
Enter fullscreen mode Exit fullscreen mode

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

<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>
Enter fullscreen mode Exit fullscreen mode
<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>
Enter fullscreen mode Exit fullscreen mode

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
  2. Case forgot/reset password (with firebase.auth.sendPasswordResetEmail)
  3. Add Authentication via social media vendors (e.g. firebase.auth.GoogleAuthProvider()
  4. Split the component into multiple components.
💖 💪 🙅 🚩
vannsl
Vannsl

Posted on October 19, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related