Nuxt Sign-in With Firebase Auth V9 Modular With FirebaseUI

April 12, 2022

Setup Nuxt With Firebase V9 Modular.

Create components/SignIn.vue.

<template>
  <div id="firebaseui-auth-container">
    <!-- <b-skeleton height="48px" v-if="loading"></b-skeleton>-->
    <v-if="loading">Loading ...</v-if="loading">
  </div>
</template>

<script>
import { GoogleAuthProvider } from "firebase/auth";
// cannot put here, else window is not defined
// import firebaseui from "firebaseui";
import "firebaseui/dist/firebaseui.css"

export default {
  data() {
    return {
      loading: true
    }
  },
  async mounted() {
    const auth = await this.$fire.getAuth()

    // const { GoogleAuthProvider } = await import("firebase/auth");
    const firebaseui = await import('firebaseui')
    // import("firebaseui/dist/firebaseui.css")

    const ui = firebaseui.auth.AuthUI.getInstance() || new firebaseui.auth.AuthUI(auth)

    const _this = this

    const config = {
      signInOptions: [
        // this.$fireModule.auth.GoogleAuthProvider.PROVIDER_ID
        GoogleAuthProvider.PROVIDER_ID
      ],
      signInFlow: 'popup',
      // signInSuccessUrl: '/',
      // tosUrl: '/tos',
      // privacyPolicyUrl: '/privacy',
      callbacks: {
        signInSuccessWithAuthResult() {
          // console.log('signInSuccessWithAuthResult')
          _this.$emit('success')
        },
        uiShown: () => {
          // console.log('uiShown')
          this.loading = false
        }
      }
    }


    ui.start('#firebaseui-auth-container', config)
  }
}  
</script>

Usage

<template>
  <div>
    <sign-in v-on:success="onSignIn()" />
  </div>
</template>

<script>
export default {
  props: ['value'],
  methods: {
    onSignIn() {
      // this.$emit('update:value', false)
      const user = this.$fire.auth.currentUser;
    },
    signOut() {
      // assume await this.$fire.getAuth() is already called
      this.$fire.auth.signOut()
    }
    /*
    async signOut() {
      const auth = await this.$fire.getAuth()
      auth.signOut()
    }
     */
  },
}
</script>

Listen to user sign-in via onAuthStateChanged, and write to vuex store.

async getAuth() {
  if (!this.auth) {
    const { getAuth, onAuthStateChanged } = await import("firebase/auth")

    const firebaseApp = await this.getApp()
    this.auth = getAuth(firebaseApp)

    onAuthStateChanged(this.auth, async (authUser) => {
      const claims = authUser ? (await authUser.getIdTokenResult(true)).claims : null
      if (authUser) {
        // I only run app check when user signin
        await fire.getAppCheck()
      }

      // store 
      await store.dispatch('onAuthStateChangedAction', { authUser, claims })
    })
  }

  return this.auth
}

Edit store/index.js

export const state = () => ({
  user: false,
})

export const mutations = {
  ON_AUTH_STATE_CHANGED_MUTATION: async (state, { authUser, claims, user }) => {
    if (authUser) {
      const data = {
        uid: authUser.uid,
        // email: authUser.email,
        displayName: authUser.displayName,
      }
      if (user) {
        data.displayName = user.name
      }

      if (claims.isAdmin) {
        data.isAdmin = true
      }

      state.user = data
    }
    else {
      state.user = false
    }

    if (process.client && window.localStorage) {
      /**
       * Auth.onAuthStateChanged is delayed by 0.35s on first load before you can access the user object
       * Alternatively you can store part of user object in window.localStorage for quicker access, but less safe
       */ 

      /*
      try {
        let data = JSON.stringify(state.user)
        localStorage.setItem('__user__', data)
      }
      catch(err) {
        console.error('localStorage fail', err)
      }
       */
    }
  },
}

export const actions = {
  async onAuthStateChangedAction({ commit }, { authUser, claims }) {

    let user = null
    if (authUser) {
      const db = await this.$fire.getFirestore() 

      // fetch user object from database
      // getItem is my custom helper funtion
      user = await db.getItem('user', authUser.uid)
    }

    commit('ON_AUTH_STATE_CHANGED_MUTATION', { authUser, claims, user })
  }
}

Access user object from any page or component.

<script>
export default {
  computed: {
    currentUser() {
      /*
      if (process.client && !this.$store.state.user && window.localStorage) {
        const json = localStorage.getItem('__user__')
        if (json) {
          try { 
            return JSON.parse(json) 
          }
          catch (err) {
            console.error('localStorage fail', err)
          }
        }
      }
       */
      return this.$store.state.user
    },  
  },
}
</script>
This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.