Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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

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

Step - 1

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

Step - 2

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' }
]

Step - 3

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

Step - 4

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)
    })
}

Step - 5

Again go back to nuxt.config.js and add the following inside the confiiig list

serverMiddleware: ['~/serverMiddleware/validateFirebaseIdToken']

Step - 6

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.
  }
}
@ChristPetitjean

This comment has been minimized.

Copy link

@ChristPetitjean ChristPetitjean commented Mar 5, 2020

How can we get current session in asyncData ? the store is populated after asyncData call and therefore current session is not authorize on firestore

@zeroows

This comment has been minimized.

Copy link

@zeroows zeroows commented Mar 17, 2020

you need to put firebase-client-init.js inside plugins folder

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment