Using Firebase Authentication in a Vue Application
In this tutorial, we are going to build a simple Vue application using Vue CLI and firebase authentication.
But first, let’s get our system set up for Vue development.
Install Vue CLI
Vue CLI requires node.js version 8.9 and above. So ensure you have the right version of node.js installed. You can check your node.js version by entering this on your console.
node -v
If you don’t have node.js installed, you can install it by heading over to the node.js website.
After setting up node.js, install Vue CLI by running the command shown below in your console:
npm install -g @vue/cli
This command installs Vue CLI globally and it can now be accessed from the terminal. To confirm the Vue CLI installation, run:
vue --version
You should get an output stating the version of your Vue installation.
If the installation was successful, run the command below to create a new Vue CLI project.
vue create vuefireapp
You will be prompted to pick a preset. For this app, the default preset is just fine. Select the default preset and let vue CLI scaffold our project.
To see what our app looks like, let’s serve it:
cd vuefireapp
npm run serve
Go to http://localhost:8080/ to view your Vue application.
Install Additional Libraries
We will be using vue router for routing between pages. Install it by running the command below:
npm install vue-router
We will be using Vuex for state management, hence, we need to install it.
npm install vuex --save
We will be using firebase for authentication, hence we need to install it.
npm install firebase --save
Create A Firebase Project
In order to use any Firebase services, we need to create a firebase project.
Head over to the firebase console and click on the create project button.
Enter a suitable name for your project in the next screen that appears:
The next screen after this is about setting up google analytics for the app. We won’t be needing that for this application, so you can continue to create the project. Firebase will take some time to set things up. When it is done, you should have a screen that looks like this:
Click the continue button to go to your firebase dashboard.
We would go back to the firebase dashboard to get some details for our application, but for now, let’s set up the UI and routing for our application.
Building the UI
We will be using bootstrap CSS for some styles, so include the CDN in public/index.html
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
We will need a header component which will house the login and logout buttons, and a link to the home page. Go to src/App.vue
Create a new AppHeader.vue
component in the src/components/
directory and paste the following into the file:
<template>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<a href="#" class="navbar-brand mb-0 h1">VueFire App</a>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
</li>
</ul>
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="btn btn-outline-warning" href="#">Sign up</a>
</li>
<li class="nav-item pl-2">
<a class="btn btn-light" href="#">Sign in</a>
</li>
</ul>
</div>
</nav>
</template>
<script>
export default {
}
</script>
Modify src/App.vue
to use the newly created AppHeader component
<template>
<div id="app">
<app-header></app-header>
</div>
</template>
<script>
import AppHeader from './components/AppHeader.vue'
export default {
name: 'app',
// Register the AppHeader Component
components: {
AppHeader
}
}
</script>
Create a new component called AppHome in src/components
directory, and add the following code to it.
<template>
<main class="jumbotron">
<h1 class="display-4">Hello, there!</h1>
<p class="lead">
Welcome to code-source.tempurl.host
Login or signup to continue
</p>
</main>
</template>
<script>
export default {
}
</script>
This component will act as our home page.
We will need to set up the vue router to serve other pages that will be created. Create a routes.js
file in /src directory
. Add the following code to it, to set up routing for the home component:
import VueRouter from 'vue-router'
import AppHome from './components/AppHome'
const routes = [
{
component: AppHome,
name: '',
path: ''
}
]
export default new VueRouter({
mode: 'history',
routes
});
Modify main.js
for Vue to use our newly setup router.Main.js
should be looking like this:
import Vue from 'vue'
// import vueRouter
import VueRouter from 'vue-router'
// import our routes
import router from './routes'
import App from './App.vue'
Vue.config.productionTip = false
// Setup Vue Router
Vue.use(VueRouter)
new Vue({
router,
render: h => h(App),
}).$mount('#app')
Modify app.vue to contain an entry point for our router.
<template>
...
<!-- Router entry point -->
<router-view></router-view>
...
</template>
http://localhost:8080/ should be looking like this:
Next, we need to create the login and signup components and route them using the Vue router. Create a new file: Login.vue in src/components directory
. Add the following to the login component:
<template>
<div class="login-box">
<form action="#" class="col-12">
<h3>Login</h3>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" class="form-control">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control" name="password" id="password">
</div>
<div class="form-group">
<button type="submit">Login</button>
</div>
</form>
</div>
</template>
<style scoped>
.login-box {
display: flex;
justify-content: center;
margin: 50px auto;
padding: 20px;
width: 500px;
box-shadow: 0 0 5px #a1a1a1;
}
</style>
<script>
export default {
}
</script>
Create a signup component at /src/components/Signup.vue
and add the following code to it:
<template>
<div class="signup-box">
<form action="#" class="col-12">
<h3>Register</h3>
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" name="username" class="form-control">
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" class="form-control">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control" name="password" id="password">
</div>
<div class="form-group">
<button type="submit">Register</button>
</div>
</form>
</div>
</template>
<style scoped>
.signup-box {
display: flex;
justify-content: center;
margin: 50px auto;
padding: 20px;
width: 500px;
box-shadow: 0 0 5px #a1a1a1;
}
</style>
<script>
export default {
}
</script>
Update our routes.js
to reflect the new components.
...
import Login from './components/Login'
import Signup from './components/Signup'
const routes = [
...
{
component: Login,
name: 'login',
path: '/login'
},
{
component: Signup,
name: 'signup',
path: '/signup'
}
]
...
Modify the AppHeader component to link the buttons to their respective component.
// AppHeader.vue
...
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<router-link class="btn btn-outline-warning" :to="{name: 'signup'}">Sign up</router-link>
</li>
<li class="nav-item pl-2">
<router-link class="btn btn-light" :to="{name: 'login'}">Sign in</router-link>
</li>
</ul>
...
Go to http://localhost:8080/signup and http://localhost:8080/login, the new components will be reflected.
Setup Vuex Store
We will use Vuex to keep state, hence we need to set it up.
Create a store.js file in the src/
directory and add the following to it:
// store.js
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
user: {}
}
});
Update src/main.js
to use our newly created store
// main.js
...
// import vuex store
import store from './store'
import App from './App.vue'
...
new Vue({
router,
store,
render: h => h(App),
}).$mount('#app')
Setup authentication from firebase dashboard
Go to your firebase console, and select the project you created earlier.
On your dashboard, click the add firebase to your web app button.
Choose a suitable name for your app in the next screen that shows:
After adding your web app name, click the register app button to continue.
You should now be at a screen showing your firebase details. keep them, we will be needing them soon.
We are going to create a firebase helper service to help with all calls to Firebase API.
Create a new services directory in the /src folder.
Create a firebaseService.js file in the services folder, and add the following to it.
// import the firebase
import firebase from "firebase";
// config data from your firebase dashboard
const CONFIG = {
apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
authDomain: "xxxxx-xxxx-xxxxx-xxxx.firebaseapp.com",
databaseURL: "https://xxxxx-xxxx-xxxxx-xxxx.firebaseio.com",
projectId: "xxxxx-xxxx-xxxxx-xxxx",
storageBucket: "xxxxx-xxxx-xxxxx-xxxx.appspot.com",
messagingSenderId: "xxxxxxxxxxx",
appId: "1:xxxxxxxxxx:web:xxxxxxxxxxxxxxxxxxx"
};
// Export methods which will be used to access the firebase api
// initalize firebase
export const init = function() {
firebase.initializeApp(CONFIG)
}
Before we can use the authentication service from Firebase, we need to enable it from our dashboard. Head over to your dashboard to enable the Firebase authentication service.
Click on the setup sign-in method, to select any sign-in method of your choice.
We will use the email/password provider, so select the edit icon beside email/password provider and toggle the enable button as seen in the image below:
Click on the Save button to continue.
Create a firebase service to handle API calls
We will create a register method which will handle registration using firebase API.
Firebase exposes a method in its auth service called createUserWithEmailAndPassword
which accepts a user’s username, email, and password to create a new user. However, the create user method doesn’t add a username. We can do that by updating the user’s profile with the username immediately after creating the user.
// firebaseService.js
...
// Export methods which will be used to access the firebase api
...
// Register method
export async function register(username, email, password) {
try {
// create user using the email and password
const response = await firebase.auth().createUserWithEmailAndPassword(email, password)
// update the user to add his username
await response.user.updateProfile({
displayName: username
})
return response.user
} catch (error) {
// alert any error that occurred in the process
alert(error)
}
}
We’ll now create a login method which will use firebase’s signInWithEmailAndPassword
Add this block of code below the register method:
// Login user
export async function login(email, password) {
try {
// log in using email and password
const response = await firebase.auth().signInWithEmailAndPassword(email, password)
// return the currently logged in user.
return response.user
} catch (error) {
alert(error);
}
}
Now that we have created these methods, let’s put them to use.
Using the Firebase Service
We are using vuex to store the currently authenticated user, hence we need to create mutations to update the current user.
Update your vuex store with the following:
// store.js
...
export const store = new Vuex.Store({
state: {
user: {}
},
mutations: {
// mutation to update currently authenticated user.
setUser(state, user) {
state.user = user;
}
}
});
Import the register method from our firebaseService to the signup component.
Add this to the script section on src/Signup.vue
:
//signup.vue
import { register } from '../services/firebaseService';
export default {
data() {
return {
// make the data for the form reactive
user: {
username: '',
email: '',
password: ''
},
// handle loading state.
isLoading: false
}
},
methods: {
// action to be performed when the register form is submitted
registerUser() {
register(this.user.username, this.user.email, this.user.password)
.then(user => {
// commit the mutation
this.$store.commit('setUser', user)
})
.then(() => {
// Go to the home page after loggin in.
this.$router.push('/');
})
;
}
}
}
Update the template to reflect the reactive data and to set submit handler for the form.
<template>
...
<form @submit.prevent="registerUser" class="col-12">
<input type="text" id="username" v-model="user.username" name="username" class="form-control" required>
...
<input type="email" id="email" name="email" v-model="user.email" class="form-control" required>
...
<input type="password" class="form-control" v-model="user.password" name="password" id="password" required>
...
<div class="form-group">
<button type="submit" v-if="!isLoading">Register</button>
<button type="button" v-else disabled>Loading...</button>
</div>
</form>
...
</template>
Let’s update the AppHeader component to show the user’s display name after signing up.
Go to your AppHeader component and update it as shown below:
<template>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<a href="#" class="navbar-brand mb-0 h1">VueFire App</a>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
</li>
</ul>
<ul class="navbar-nav ml-auto" v-if="!isAuthenticated">
<li class="nav-item">
<a class="btn btn-outline-warning" href="#">Sign up</a>
</li>
<li class="nav-item pl-2">
<a class="btn btn-light" href="#">Sign in</a>
</li>
</ul>
<ul class="navbar-nav ml-auto" v-else>
<li class="nav-item">
<a href="#">{{ user.displayName }}</a>
</li>
</ul>
</div>
</nav>
</template>
<script>
export default {
computed: {
user() {
return this.$store.state.user
},
isAuthenticated() {
return this.$store.state.user.hasOwnProperty('id')
}
}
}
</script>
Now the header should be updated with the display name of the currently authenticated user.
Next up, we need to add Logout functionality to the application.
Modify the firebaseService to add a logout function:
// firebaseService.js
...
// Log user out
export async function logout () {
await firebase.auth().signOut()
}
Let’s use this function in the AppHeader component to allow users to logout.
Update the AppHeader component with this code:
// AppHeader.vue
...
<script>
import {logout} from '../services/firebaseService'
export default {
...
methods: {
logout() {
logout().then(() => {
// update the authenticated user
this.$store.commit('setUser', {});
});
}
}
}
</script>
And finally, we need to add login functionality.
The login function has already been in our firebaseService, so let’s use it in our login component.
Modify the login component to use the login function from firebaseService.
<template>
<div class="login-box">
<form action="#" @submit.prevent="loginUser" class="col-12">
<h3>Login</h3>
<div class="form-group">
<label for="email"<Email</label>
<input type="email" v-model="user.email" id="email" class="form-control" required>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" v-model="user.password" class="form-control" name="password" id="password" required>
</div>
<div class="form-group">
<button type="submit" v-if="!isLoading">Register</button>
<button type="button" v-else disabled>Loading...</button>
</div>
</form>
</div>
</template>
<script>
import { login } from "../services/firebaseService";
export default {
data() {
return {
user: {
email: '',
password: '',
},
isLoading: false
}
},
methods: {
loginUser() {
this.isLoading = true
login(this.user.email, this.user.password)
.then(user => {
this.$store.commit('setUser', user);
this.isLoading = false;
})
.then(() => {
// Go to the home page after loggin in.
this.$router.push('/');
})
;
}
}
}
</script>
At this point, we have successfully used firebase for authenticating users into our application.
However, if the user reloads the page, the currently logged in user will be cleared. We need to fetch the currently authenticated user from firebase whenever our app is loaded. To do that, add update the firebase service to check for the currently authenticated user:
// firebaseService.js
// store the firebase initialization status
let initializationComplete = false;
// initalize firebase
export function init() {
firebase.initializeApp(CONFIG)
// check the initialization status
firebase.auth().onAuthStateChanged(() => {
initializationComplete = true;
});
}
export function getLoggedInUser() {
// resolve with the currently authenticated user.
return new Promise((resolve, reject) => {
let initializationInterval = setInterval(() => {
if (initializationComplete) {
clearInterval(initializationInterval);
resolve(firebase.auth().currentUser)
}
},10);
})
}
The getLoggedInUser function should be called from App.vue which is mounted every time the page is loaded.
// App.vue
...
export default {
name: 'app',
// Register the AppHeader Component
created() {
getLoggedInUser().then(user => {
if (user) {
this.$store.commit('setUser', user)
}
});
},
components: {
AppHeader
}
}
Congratulations!!!
You have successfully used firebase authentication service on your application!
conclusion
I hope you learned a few things about Vue. Every article can be made better, so please leave your suggestions and contributions in the comments below. If you have questions about any of the steps, please do ask also in the comments section below.
[epcl_columns] [epcl_button label=”Code source” url=”https://github.com/Dunebook/learn-vue-fire-auth” type=”outline” color=”red” size=”large” icon=”fa-github” target=”_blank” rel=”dofollow”][/epcl_button] [epcl_button label=”Project Demo” url=”https://vigilant-allen-8ff2dc.netlify.com/” type=”outline” color=”red” size=”large” icon=”fa-github” target=”_blank” rel=”dofollow”] [/epcl_columns]