Nuxt JS: Keep user logged in with Firebase Auth and also fetch the necessary User data from Cloud Firestore and put it in your Vuex store if the user is already logged in before rendering the page
Install the required packages.
-
If you use
npm
npm i firebase firebase-admin cookies js-cookie
-
If you use
yarn
yarn add firebase firebase-admin cookies js-cookie
Inside the plugins
folder create:
auth-cookie.js
Which will hold
import Cookies from 'js-cookie'
import { auth } from '~/plugins/firebase-client-init'
export default (context) => {
auth.addAuthTokenListener((idToken) => {
// 3600 sec = 0.04166667 Days
Cookies.set('__session', idToken, { expires: idToken ? 0.04166667 : 0 })
})
}
Now add the plugin to your nuxt.config.js
plugins: [
{ src: '~/plugins/auth-cookie', mode: 'client' }
]
Create a folder called services
in the root of the project.
Inside this folder create:
firebase-client-init.js
Which will holde:
import firebase from 'firebase/app'
import 'firebase/performance'
import 'firebase/firestore'
import 'firebase/auth'
import 'firebase/functions'
// Initialize firebase
const config = {
// your config
}
// if (!firebase.apps.length) {
// firebase.initializeApp(config)
// }
export default !firebase.apps.length
? firebase.initializeApp(config)
: firebase.app()
// Initialize Performance Monitoring and get a reference to the service
// export const perf = firebase.performance()
export const db = firebase.firestore()
export const auth = firebase.auth()
export const functions = firebase.app().functions('asia-east2')
Then create firebase-admin-init.js
Which will hold:
const admin = require('firebase-admin')
const key = require('../serviceAccounts.json') // You will get it inside your firebase project settings
// eslint-disable-next-line import/no-mutable-exports
let adminApp
if (admin.apps.length > 0) {
adminApp = admin
} else {
adminApp = admin.initializeApp({
credential: admin.credential.cert(key)
})
}
export default adminApp
Create a serverMiddleware
folder in the root.
Inside that folderr, create a file validateFirebaseIdToken.js
Which will hold the following code:
import Cookies from 'cookies'
import admin from '../services/firebase-admin-init'
export default (req, res, next) => {
getIdTokenFromRequest(req, res).then((idToken) => {
if (idToken) {
addDecodedIdTokenToRequest(idToken, req).then(() => {
fetchUserDataFirestore(req.user, req).then(() => {
next()
})
})
} else {
next()
}
})
}
function getIdTokenFromRequest(req, res) {
if (
req.headers.authorization &&
req.headers.authorization.startsWith('Bearer ')
) {
// console.log('Found "Authorization" header')
// Read the ID Token from the Authorization header.
return Promise.resolve(req.headers.authorization.split('Bearer ')[1])
}
return new Promise((resolve) => {
const cookie = new Cookies(req, res)
// console.log(cookie.get('__session'))
if (typeof cookie.get('__session') !== 'undefined') {
// console.log('Found "__session" cookie')
// Read the ID Token from cookie.
resolve(cookie.get('__session'))
} else {
resolve()
}
})
}
/**
* Returns a Promise with the Decoded ID Token and adds it to req.user.
*/
function addDecodedIdTokenToRequest(idToken, req) {
return admin
.auth()
.verifyIdToken(idToken)
.then((decodedIdToken) => {
// console.log('ID Token correctly decoded', decodedIdToken)
req.user = decodedIdToken
})
.catch((error) => {
console.error('Error while verifying Firebase ID token:', error)
})
}
function fetchUserDataFirestore(user, req) {
return admin
.firestore()
.collection('users')
.doc(user.uid)
.get()
.then((doc) => {
if (doc.exists) {
const user = {
name: doc.data().name,
email: doc.data().email.toLowerCase(),
petName: doc.data().petName,
uid: doc.id
}
// Set user request data
req.userData = user
}
})
.catch((error) => {
console.error(error.message)
})
}
Again go back to nuxt.config.js
and add the following inside the confiiig list
serverMiddleware: ['~/serverMiddleware/validateFirebaseIdToken']
Go to store/index.js
annd inside action add this
nuxtServerInit({ commit }, { req }) {
if (req.userData) {
// We have all the needed user details. Lets push it to Vuex
commit('user/updateID', req.userData)
// and so on.....
// console.log(req.userData) to see what you are getting.
}
}
How can we get current session in asyncData ? the store is populated after asyncData call and therefore current session is not authorize on firestore