In this tutorial, we will create an application with authentication using Vue and Firebase Authentication UI. You can add what you’ll learn here to my previous post titled “Fullstack with Firebase and Vue.js

Firebase Authentication

Implementing your own authentication system is the most risky decision you could ever made when developing your applications. It is challenging to create an authentication module covering login, logout, registration, reset password and forgot password mechanisms while balancing the ease of use and maintainability of it. Authentication is your first line of defense against malicious attacks, so why risk it?

Fortunately, Firebase provides an easy way to add authentication to your application. You can use the traditional email and password combo or use social network login such as Google, Facebook, Twitter and Github. They also have anonymous and phone authentication. Don’t waste your resources building your own authentication system. Let’s build a Vue application that will authenticate the users using Firebase Authentication service.

Create Vue project

First, make sure you have Node.js and NPM installed on your local system. Next, to generate Vue.js projects, make sure that Vue CLI is installed. If not, you can install it like this:

npm install -g @vue/cli

We will create a project using the cli. The cli will bootstrap an application using a the default preset. You can also manually select features that you want to be included in the application.

vue create secureapp

Select “Manually select features”. Make sure to add the “Router” option

Manually selecting features in Vue CLI

The process of initializing a new project will take a few minutes. Switch to your Facebook tab and quickly scroll over your friends’ self-pity posts. Once the initialization is done switch back here and do this commands:

cd secureapp
npm run serve

That will fire up the built-in web server and host your application. You can access it by going to http://localhost:8080. The initialized project will look something like this:

Default view from the contents of HelloWorld.vue file

Leave your terminal for now and let’s now create a Firebase project.

Create Firebase project

Sign up to Firebase and create a new project. Name your project whatever you like. Once done, go to your project and proceed to the Authentication menu. Click Sign-in method and enable Google as authentication provider. You can add providers if you want but we will deal with Google for now.

Firebase Authentication Sign-in methods

Make sure to hit save.

Let’s start coding

Now that we have our Firebase Authentication ready, let’s add Firebase to our application. Go back to your terminal and enter this command to add the firebase package:

npm install firebase firebaseui --save

Create a file under src folder named firebase.js and put this code

export const config = {
apiKey: "xxxxxxxx",
authDomain: "xxxxxxxx",
databaseURL: "xxxxxxxx",
projectId: "xxxxxxxx",
storageBucket: "xxxxxxxx",
messagingSenderId: "xxxxxxxx"
}

Replace the masked part with the values provided to you by Firebase. To get these values, go back to the Firebase project overview and click Add App. Choose the web platform and look for this part:

Firebase project settings for Web

After that, open src/main.js and update the code to look like this:

import Vue from 'vue'
import App from './App.vue'
import router from './router'

import firebase from "firebase";
import { config } from "./firebase";

Vue.config.productionTip = false

router.beforeEach((to, from, next) => {

    if(!to.meta.protected) { //route is public, don't check for authentication
        next()
    } else { //route is protected, if authenticated, proceed. Else, login
        firebase.auth().onAuthStateChanged((user) => {
            if(user) {
                next()
            } else {
                router.push('/login')
            }
        })
    }
})

new Vue({
    router,
    created() {
        firebase.initializeApp(config)
    },
    render: h => h(App)
}).$mount('#app')

There’s a lot happening here. First we imported the firebase and firebaseui packages and initialized a firebase instance in the created hook of our Vue app. Then, we called the beforeEach method of vue-router to execute a block of code before rendering the corresponding component of a route. We will perform authentication checking for every route except those that are not protected. If the route requires authentication, we will check if the user has logged before going to it. If not, redirect them to the login page. We will define the routes and their meta properties later on.

Firebase UI

FirebaseUI is a library built on top of the Firebase Authentication SDK that provides drop-in UI flows for use in your app. This will give us a login user interface without coding our own. We just need to provide a container to hold the provided UI.

Create a file named Login.vue under src/views. Put this code in the newly created file:

<template>
    <div>
        <div id="firebaseui-auth-container"></div>
    </div>
</template>

<script>
import firebase from "firebase"
import * as firebaseui from "firebaseui"
import "../../node_modules/firebaseui/dist/firebaseui.css"

export default {
    mounted() {
        let uiConfig = {
            signInOptions: [{
                provider: firebase.auth.GoogleAuthProvider.PROVIDER_ID
}],
callbacks: {
                signInSuccessWithAuthResult() {
localStorage.setItem('authenticated', true)
window.location.href = '/'
}
}
        }
        var ui = new firebaseui.auth.AuthUI(firebase.auth())
        ui.start("#firebaseui-auth-container", uiConfig)
    }
}
</script>


We initialized a Firebase UI and placed it in a div container. We used Google as the login provider. A localStorage item will be created to hold our authentication state, to prove that we really logged in. If the authentication is successful, the user will be redirected to the home page.

To logout, create a file named Logout.vue under src/views. Put this code in the newly created file:

<template>
<div>
<span>Logging out . . .</span>
</div>
</template>

<script>
import firebase from "firebase"
export default {
mounted() {
firebase.auth().signOut().then(() => {
localStorage.setItem('authenticated', false)
window.location.href = '/'
})
}
}
</script>

Once signed out, the user will be redirected back to the home page and the localStorage item holding the authentication state will be updated to false.

Navigation

Open src/App.vue and update it to look like this:

<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link> |
<router-link to="/login" v-if="!authenticated">Login</router-link>
<router-link to="/logout" v-else>Logout</router-link>
</div>
<router-view/>
</div>
</template>

<script>
export default {
    computed: {
        authenticated() {
            return JSON.parse(localStorage.getItem('authenticated'))
        }
    }
}
</script>


<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#nav {
padding: 30px;
}

#nav a {
font-weight: bold;
color: #2c3e50;
}

#nav a.router-link-exact-active {
color: #42b983;
}
</style>

We assigned the authentication state to a computed property called “authenticated“. Logout menu will only be displayed to users who already logged in.

Routing and Authentication

To connect all what we did, we will now define the routes and set the About component as a protected route. Open src/router.js and update the content to look like this:

import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
import About from './views/About.vue'
import Login from './views/Login.vue'
import Logout from './views/Logout.vue'

Vue.use(Router)

export default new Router({
    mode: 'history',
    base: process.env.BASE_URL,
    routes: [
        {
            path: '/',
            name: 'home',
            component: Home
        },
        {
            path: '/about',
            name: 'about',
            component: About,
            meta: {
                protected: true
            }
        },
        {
            path: '/login',
            name: 'login',
            component: Login
        },
        {
            path: '/logout',
            name: 'logout',
            component: Logout
        }
    ]
})

We added routes for the login and logout pages we created a while ago. Look closely on this part:

{
path: '/about',
name: 'about',
component: About,
    meta: {
     protected: true
    }
}

We added a meta property on the /about route with a subproperty protected which has a value of true. The code we defined on main.js a while ago will check for this property. If the route is protected and the user has not yet logged in, the user will be redirected to the login page.

Demo, demo, demo

Login to your Vue app using Firebase UI

Conclusion

That’s it, you now have a working app with authentication using only Firebase and Vue.js. This is a simplified tutorial and demo. You can use vuex and vuex-persistedstate to minimize the calls to Firebase API. Join me next time in developing fullstack applications using our tools of choice: Firebase and Vue.js. See you next time!


2 Comments

Adam C · August 2, 2019 at 6:50 am

Hi. Great series. The prior expense tutorial went super smoothly but this one I am hitting three errors that prevent the login stuff from working or displaying. I’ve redone the tutorial a few times without luck.

I can load the home page and the about link routes me to the login page. The problem is that the login page is a blank page with the navigation. I can see that the html div s render in the browser inspector but no google login appears.

So here is one error:
“`
error: ‘config’ is defined but never used (no-unused-vars) at src\views\Login.vue:11:10:
9 | import firebaseui from “firebaseui”
10 | import “../../node_modules/firebaseui/dist/firebaseui.css”
> 11 | import { config } from “../firebase”
| ^
12 |
13 | export default {
14 | mounted() {
“`
and another:
“`
error: ‘self’ is assigned a value but never used (no-unused-vars) at src\views\Login.vue:15:13:
13 | export default {
14 | mounted() {
> 15 | let self = this
| ^
16 |
17 | let uiConfig = {
18 | signInOptions: [{
“`
and finally:
“` warning in ./src/views/Login.vue?vue&type=script&lang=js&
“export ‘default’ (imported as ‘firebaseui’) was not found in ‘firebaseui’ “`

Both of these tutorials ended up running on port 8081 for some reason. I think I have another project that is some how blocking that. Not sure if that is a consideration since it seems to work for the 1st tutorial but thought I’d mention. Any help would be appreciated but I understand how difficult it can be. Thanks!!!

    Jacob Fernandez · August 2, 2019 at 5:58 pm

    Adam,

    Firebase UI package has new updates and importing it to the project should be like this:

    import * as firebaseui from ‘firebaseui’

    I also made changes to the snippets regarding unused imports and variable declarations. Happy to hear that you’re loving the tutorials.

Leave a Reply

%d bloggers like this: