How to make basic Authentication in Vue.js using Google Firebase

Michal Jurkowski
8 min readDec 21, 2017

--

Sometimes as developers we want to create simple prototype to make MVP(Minimum Value Product) of our Start-Up idea. If you know basic usage of Javascript and Vue.js you can prepare your mvp base in minutes.

In this project we want to prepare basic Authentication and decide where can go logged user and where can go guest. To this solution I’m going to use Google Firebase — service mixed tools to build apps fast, without managing infrastructure.

Before we start

Before we start you have to go to console.firebase.google.com and create a new project. After that on start screen you can check “Add Firebase to your web app” and then you can see javascript configuration. COPY IT!

Next think is configure your Auth module. From sidebar check DEVELOP -> Authentication. Go to SIGN-IN METHOD card and setup 3 methods: Email/Password, Google and Facebook. Follow the instruction in popups to property configuration. If you have a problem, let me know in comment — I’m going to prepare video about that.

Step 1: Initialise new Vue project

To init our project I’m going to use Vue CLI with webpack template. All thinks about CLI you can find here.

$ vue init webpack auth-project

During installation CLI will ask you about project name, description, author etc. If you want to prepare unit test or linter — just change answer for yes. Important — set Vue build for standalone and install Vue router for Yes.

? Project name auth-project
? Project description A Vue.js project
? Author Michal Jurkowski
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? No
? Set up unit tests No
? Setup e2e tests with Nightwatch? No
? Should we run `npm install` for you after the project has been created? (recommended) npm
vue-cli · Generated “auth-project”.

Next we have to go into our project and install extra npm modules to use Vuex(Vue State Management Pattern), Firebase SDK and Firebase UI(firebase build-in authentication component).

$ cd auth-project
$ npm install vuex firebase firebaseui --save

Step 2: Prepare simple Views

Next step is create in our src directory new directory with called views. Here we want to create 3 simple views: Home, Auth, Dashboard. In our project you will start at Home — this view is enable for all users and guests. From there you can go to login — Auth where you can choose Auth method. Dashboard view is place for logged users — important restriction.

From src/components I remove HelloWorld.vue — it is not necessary to work property in our project.

My 3 components in src/views looks like that:

# src/views/Home.vue<template>
<div>
<h1>Hello!</h1>
<p>What you want to do?</p>
<router-link to="/auth">Sign in</router-link>
</div>
</template>

<script>
export default {
name: 'home'
}
</script>

<style scoped>

</style>

As you can see I use <router-link> to prepare anchor to our auth page.

# src/views/Auth.vue<template>
<div>
<h1>Sign in</h1>
</div>
</template>

<script>
export default {
name: 'auth',
}
</script>

<style scoped>

</style>

In Step 5 we are going to implement Firebase UI here.

And last our file is Dashboard:

# src/views/Dashboard.vue<template>
<div>
<h1>Hello USER!</h1>
</div>
</template>

<script>
export default {
name: 'dashboard'
}
</script>

<style scoped>

</style>

Step 3: Create simple routing

Now we have to create simple routing with restrictions. Look at our routes — for /auth and /dashboard I add extra property meta with 2 keys: guestOnly(if it’s true and user are logged we will redirect him to view for user — ex. if user want to go for login page if is logged) and requireAuth(if it’s true and user are not logged, we have to redirect him to login page).

For better practice I use History Mode from HTML5. More information you can find here.

# src/router/index.jsimport Vue from 'vue'
import Router from 'vue-router'

import Auth from '@/views/Auth'
import Dashboard from '@/views/Dashboard'
import Home from '@/views/Home'

Vue.use(Router)

var routes = [
{ path: '/home', name: 'home', component: Home },
{ path: '/auth', name: 'auth', component: Auth, meta: { guestOnly: true } },
{ path: '/dashboard', name: 'dashboard', component: Dashboard, meta: { requireAuth: true } },
{ path: '*', redirect: '/home' }
]

export const router = new Router({
mode: 'history',
routes
})

And now we have to connect our router to main Vue instance in main.js

import Vue from 'vue'

import App from '@/App'

import {router} from '@/router'

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})

Step 4: Prepare Vuex Store

Now we have ready simple SPA(Single Page Application). But before we begin work with firebase, we have to prepare our Store for user data. Best practice in Vue is work with state management stores. All things about Vuex you can read here.

First good practice is make independent user module to mange user states. For this I create simple file structure src/store with main store( index.js ) and user module ( modules/user.js)

In this example I put this structure because it can help you to understand namespaces and how to scalable your application. Each module is kind of store of data. If you separate each module then all modification in you application can be easier.

# src/store/index.jsimport Vue from 'vue'
import Vuex from 'vuex'

import user from './modules/user'

Vue.use(Vuex)

export const store = new Vuex.Store({
state: {

},
getters: {

},
mutations: {

},
actions: {

},
modules: {
user
}
})

As you can see in this file I prepare main Vuex Store and put user module.

# src/store/modules/user.jsimport Vue from 'vue'

const state = {
user: null
}

const getters = {
user: state => state.user,
isLogged: state => (state.user !== null)
}

const mutations = {
setUser: (state, user) => {
state.user = user
}
}

const actions = {

}

export default {
namespaced: true,
state,
getters,
mutations,
actions
}

And now we can init our store in main.js

# main.jsimport Vue from 'vue'

import App from '@/App'

import {router} from '@/router'
import {store} from '@/store'

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
el: '#app',
store,
router,
template: '<App/>',
components: { App }
})

Step 5: Build auth class

If you configure you firebase project(instruction is on the begin of this post), then we can start to create auth class. We’re going to make single file where all auth methods will be presented.

# src/auth.jsimport firebase from 'firebase'
import firebaseui from 'firebaseui';

const config = {
apiKey: '#######',
authDomain: '#######',
databaseURL: '#######',
projectId: '#######',
storageBucket: '#######',
messagingSenderId: '#######'
};

const auth = {
context: null,
uiConfig: null,
ui: null,

init(context) {
this.context = context;

firebase.initializeApp(config);
this.uiConfig = {
signInSuccessUrl: 'dashboard',
signInOptions: [
firebase.auth.FacebookAuthProvider.PROVIDER_ID,
firebase.auth.GoogleAuthProvider.PROVIDER_ID,
firebase.auth.EmailAuthProvider.PROVIDER_ID
]
}
this.ui = new firebaseui.auth.AuthUI(firebase.auth());

firebase.auth().onAuthStateChanged((user) => {
this.context.$store.dispatch('user/setCurrentUser')

let requireAuth = this.context.$route.matched.some(record => record.meta.requireAuth)
let guestOnly = this.context.$route.matched.some(record => record.meta.guestOnly)

if(requireAuth && !user) this.context.$router.push('auth')
else if (guestOnly && user) this.context.$router.push('dashboard')
});
},
authForm(container) {
this.ui.start(container, this.uiConfig);
},
user() {
return this.context ? firebase.auth().currentUser : null;
},
logout() {
firebase.auth().signOut();
}
}

export default auth;

Remember to set config with your firebase keys.

Simple explanation about methods:

init(context) — we want to fire it when application will be ready. Then we make an access to main vue instance(provided by context) and initialise firebase and firebase ui with configuration. Firebase UI is initialised with 3 methods of Auth — Facebook login, Google login and Email login. And we attach to property place on our website. Remember to set signInSuccessUrl for redirect after login. I attach extra listener for user auth state — if it change I want to update user info in our store and look where user is currently(routing) and if he can be there. It is situation if user is logged and type in address bar /auth to force this view. This rule redirect him to /dashboard because he is logged.

authForm() — this method execute build of Firebase UI in container

user() — will return current user object if it exist

logout() — it is Firebase method to logout user

But look out! In init we use this.context.$store.dispatch('user/setCurrentUser') but in store we don’t have it! So we modify out src/store/modules/user.js

# src/store/modules/user.jsimport Vue from 'vue'
import auth from '@/auth';

const state = {
user: null
}

const getters = {
user: state => state.user,
isLogged: state => (state.user !== null)
}

const mutations = {
setUser: (state, user) => {
state.user = user
}
}

const actions = {
setCurrentUser: ({ commit }) => {
commit('setUser', auth.user())
}

}

export default {
namespaced: true,
state,
getters,
mutations,
actions
}

Next thing is update router to detect permission for guests and users.

# src/router/index.jsimport Vue from 'vue'
import Router from 'vue-router'
import auth from '@/auth'

import Auth from '@/views/Auth'
import Dashboard from '@/views/Dashboard'
import Home from '@/views/Home'

Vue.use(Router)

var routes = [
{ path: '/home', name: 'home', component: Home },
{ path: '/auth', name: 'auth', component: Auth, meta: { guestOnly: true } },
{ path: '/dashboard', name: 'dashboard', component: Dashboard, meta: { requireAuth: true } },
{ path: '*', redirect: '/home' }
]

export const router = new Router({
mode: 'history',
routes
})

router.beforeEach((to, from, next) => {
let currentUser = auth.user()
let requireAuth = to.matched.some(record => record.meta.requireAuth)
let guestOnly = to.matched.some(record => record.meta.guestOnly)

if (requireAuth && !currentUser) next('auth')
else if (guestOnly && currentUser) next('dashboard')
else next()
})

I attach in the end of file router.beforeEach to check our rules and redirect if it is necessary.

And last place is main.js where we have to init our auth module.

# src/main.jsimport Vue from 'vue'

import App from '@/App'

import {router} from '@/router'
import {store} from '@/store'
import auth from '@/auth'

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
el: '#app',
store,
router,
beforeCreate () {
auth.init(this)
},
template: '<App/>',
components: { App }
})

Step 6: Make Auth view

To make Auth view we have to prepare container for them and after mounted execute authForm from auth class. In addition we can import firebase ui css.

# src/views/Auth.vue<template>
<div>
<h1>Sign in</h1>
<div id="firebaseui-auth-container"></div>
</div>
</template>

<script>
import auth from '@/auth'

export default {
name: 'auth',
mounted() {
auth.authForm('#firebaseui-auth-container')
}
}
</script>

<style>
@import "../../node_modules/firebaseui/dist/firebaseui.css";
</style>

After that our Auth View looks like that:

Step 7: Show user details in Dashboard

Last think is prepare Dashboard view and put there user details.

# src/views/Dashboard.vue<template>
<div v-if="user">
<h1>Hello USER!</h1>
<img :src="user.photoURL" width="100"> <br>
<h3>{{user.displayName}}</h3>
<p>{{user.email}}</p>
<button @click="logOut">Log out</button>
<br><br><br>
<pre>{{user}}</pre>
</div>
</template>

<script>
import auth from '@/auth'

export default {
name: 'auth-success',
computed: {
user() {
return this.$store.getters['user/user']
}
},
methods: {
logOut() {
auth.logout()
}
}
}
</script>

<style scoped>
img {
border-radius: 50px;
}

h3 {
margin-bottom: 0;
}

p {
margin-top: 0;
}

pre {
text-align: left;
}
</style>

Finally it looks like that:

SUMMARY

As you can see, that prototype in Vue are very easy. If you use some external tools like Firebase you can make you apps very fast for simple distribution. All sources you can find on public repository on Github

Feel free to comment my solution!

--

--