I'm trying to authenticate a Vue.js app using firebase.
I have an issue where if trying to access a login-protected URL directly while logged in, the router will load and check for auth state before firebase.js has time to return the auth response. This results in the user being bounced to the login page (while they are already logged in).
How do I delay vue-router navigation until auth state has been retrieved from firebase? I can see that firebase stores the auth data in localStorage, would it be safe to check if that exists as a preliminary authentication check? Ideally the end result would be to show a loading spinner or something while the user is authenticated, then they should be able to access the page they navigated to.
router/index.js
let router = new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/example',
name: 'Example',
component: Example,
beforeEnter: loginRequired
}
})
function loginRequired (to, from, next) {
if (authService.authenticated()) {
next()
} else {
next('/login')
}
}
auth.js
import * as firebase from 'firebase'
var config = {
// firebase config
}
firebase.initializeApp(config)
var authService = {
firebase: firebase,
user: null,
authenticated () {
if (this.user == null) {
return false
} else {
return !this.user.isAnonymous
}
},
setUser (user) {
this.user = user
},
login (email, password) {
return this.firebase.auth().signInWithEmailAndPassword(email, password)
.then(user => {
this.setUser(user)
})
},
logout () {
this.firebase.auth().signOut().then(() => {
console.log('logout done')
})
}
}
firebase.auth().onAuthStateChanged(user => {
authService.setUser(user)
})
export default authService
app.vue
<template>
<div id="app">
<p v-if="auth.user !== null">Logged in with {{ auth.user.email }}</p>
<p v-else>not logged in</p>
<router-view v-if="auth.user !== null"></router-view>
</div>
</template>
<script>
import authService from './auth'
export default {
name: 'app',
data () {
return {
auth: authService
}
}
}
</script>
Firebase always triggers an auth state change event on startup but it's not immediate.
You'll need to make authService.authenticated
return a promise in order to wait for Firebase to complete its user/auth initialisation.
const initializeAuth = new Promise(resolve => {
// this adds a hook for the initial auth-change event
firebase.auth().onAuthStateChanged(user => {
authService.setUser(user)
resolve(user)
})
})
const authService = {
user: null,
authenticated () {
return initializeAuth.then(user => {
return user && !user.isAnonymous
})
},
setUser (user) {
this.user = user
},
login (email, password) {
return firebase.auth().signInWithEmailAndPassword(email, password)
},
logout () {
firebase.auth().signOut().then(() => {
console.log('logout done')
})
}
}
You don't need to call setUser
from the signInWith...
promise as this will already be handled by the initializeAuth
promise.