Firebase Auth with SSR

This module provides an option for the easy setup of server-side authentication as described in this article of the official Documentation.

Please Note:

This does not authenticate the Firebase Client SDK on the server. While you will be able to know if a user is logged in or not and have access to its simplified properties, you won't be able to do authenticated calls on server-side.


This means that all calls on server-side (e.g. fetching data via Firestore in fetch-hooks), which are protected by security rules, will still fail with insufficient privileges.


Reason for this is that the Firebase JS SDK is a client-side library that is not built for authenticating multiple users. See steps 4 and 5 for an experimental approach to solve this issue.

Step 0 - Install Dependencies

Install firebase-admin and @nuxtjs/pwa:

yarn add firebase-admin @nuxtjs/pwa
npm install firebase-admin @nuxtjs/pwa

Step 1 - Enable SSR functionality and configure workbox to include the auth service worker

Use the auth.ssr option.

In nuxt.config.js:

nuxt.config.js
module.exports = {
  // ...
  modules: [
    // ...
    '@nuxtjs/pwa',
    '@nuxtjs/firebase'
  ],
  firebase: {
    // ...
    services: {
      auth: {
        ssr: true
      }
      // ...
    }
  },
  pwa: {
    // disable the modules you don't need
    meta: false,
    icon: false,
    // if you omit a module key form configuration sensible defaults will be applied
    // manifest: false,

    workbox: {
      importScripts: [
        // ...
        '/firebase-auth-sw.js'
      ],
      // by default the workbox module will not install the service worker in dev environment to avoid conflicts with HMR
      // only set this true for testing and remember to always clear your browser cache in development
      dev: false
    }
  }
},

Step 2 - Setup Mutations and/or Actions to handle User authentication

If you don't rely on a full firebase.User object you can reuse the client action/mutation configured for auth.initialize.
See Step 3 to verify if required properties are present.

Example action:

async onAuthStateChangedAction({ commit, dispatch }, { authUser, claims }) {
  if (!authUser) {
    await dispatch('cleanupAction')

    return
  }

  // you can request additional fields if they are optional (e.g. photoURL)
  const { uid, email, emailVerified, displayName, photoURL } = authUser

  commit('SET_USER', {
    uid,
    email,
    emailVerified,
    displayName,
    photoURL, // results in photoURL being undefined for server auth
    // use custom claims to control access (see https://firebase.google.com/docs/auth/admin/custom-claims)
    isAdmin: claims.custom_claim
  })
}

Example mutation:

ON_AUTH_STATE_CHANGED_MUTATION(state, { authUser, claims }) {
  // you can request additional fields if they are optional (e.g. photoURL)
  const { uid, email, emailVerified, displayName, photoURL } = authUser
  
  state.authUser = {
    uid,
    displayName,
    email,
    emailVerified,
    photoURL: photoURL || null, // results in photoURL being null for server auth
    // use custom claims to control access (see https://firebase.google.com/docs/auth/admin/custom-claims)
    isAdmin: claims.custom_claim
  }
}

Step 3 - Retrieve the server user

In the nuxtServerInit action in your vuex store you can now access the authUser under the res.locals.user property as shown below.

Be aware

The server user object is not a full firebase.User, since it is reproduced from the user claims, it provides only the following properties:

  • uid: The users uid
  • email: The users email
  • emailVerified: If the email was verified
  • displayName: The users display name
  • allClaims: All claims from the admin.auth.DecodedIdToken
  • idToken: The current idToken
// Store action called nuxtServerInit:
async nuxtServerInit({ dispatch, commit }, { res }) {
  if (res && res.locals && res.locals.user) {
    const { allClaims: claims, idToken: token, ...authUser } = res.locals.user

    await dispatch('onAuthStateChangedAction', {
      authUser,
      claims,
      token
    })

    // or

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

That's it. You receive a server-verified authUser object and can do with it whatever you want in nuxtServerInit.

Step 4 - (Experimental) Authorize the admin SDK

Experimental Feature: Can be buggy, do not use in production!

If you authorize the admin SDK the authUser will be augmented to a full admin.auth.UserRecord with an additional allClaims property.

Step 5 - (Experimental) Enable server side client SDK login

Experimental Feature: Can be buggy, do not use in production!

If you need client SDK services for hydration on page load you can enable this feature in the options.