nuxt-community / firebase-module

🔥 Easily integrate Firebase into your Nuxt project. 🔥
https://firebase.nuxtjs.org
MIT License
640 stars 98 forks source link

Static site, middleware doesn't function on page load #329

Closed cliffordh closed 3 years ago

cliffordh commented 3 years ago

If I access https://mysite.com/admin

The middleware fails to prevent access. However, if I'm running in dev, "npm run dev" then things behave as expected. It's only when I do a "npx nuxt generate", "npm run start" and serve a static site that the middleware fails. I added the "process.client" stuff to the middleware because npx nuxt generate was failing without it.

Any ideas?

Here is the relevant nuxt.config.js for nuxtjs/firebase:

auth: {
            persistence: 'local', // default
            initialize: {
              onAuthStateChangedAction: 'onAuthStateChangedAction',
              onAuthStateChangedMutation: 'setUser',
            },
            ssr: false, // default
          },

Here is my middleware:

export default ({ store, error }) => {
  console.log('middleware authorization check ***')
  if (!store.state.admin) {
    if (process.client) {
      console.log('restricted area')
      error({
        message: 'Restricted area',
        statusCode: 403,
      })
    }
  }
}

Here is my store:

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

export const getters = {
  isAdmin: (state) => {
    return state.admin
  },
  user: (state) => {
    return state.user
  },
}

export const mutations = {
  setAdmin(state, isAdmin) {
    state.admin = isAdmin
  },
  setUser(state, authUser) {
    if (authUser == null) {
      state.user = null
      state.admin = false
    } else {
      const { uid, email, emailVerified, displayName, photoURL } = authUser
      state.user = {
        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,
      }
    }
  },
}

export const actions = {
  onAuthStateChangedAction({ commit }, { authUser, claims }) {
    console.log('nuxt/firebase action invoke')
    if (!authUser) {
      console.log('setAdmin: false')
      commit('setAdmin', false)
      commit('setUser', null)
      // claims = null
      // Perform logout operations
    } else {
      // Do something with the authUser and the claims object...

      //      authUser.getIdTokenResult().then((idTokenResult) => {
      // Get the decoded claims.
      //        const { claims } = idTokenResult
      // Get the "admin" attribute.
      const { admin } = claims
      commit('setAdmin', admin)
      console.log('setAdmin: ' + admin)
      //      })
    }
  },
lupas commented 3 years ago

Hey @cliffordh

Please excuse the late response.

In Nuxt "static" mode there is no server. Respectively the "server" is run once during statically pre-rendering the pages. During that time, there is no request from any user and therefore no authentication can be done.

Further, in static mode, the initial page load won't trigger the middleware, because that is simply a pre-rendered html page. There is no javascript (middleware) that is run before the html file is loaded.

The middleware is only triggered before routing to another route.

So your code won't work. Not a Firebase issue but a Nuxt specific (resp. nature of pre-rendering) issue in general, so if you have further questions to this I'd advise you to rather ask this in the Nuxt community (e.g. their Discord server).

Imho you have two options:

1) Switch to SPA mode or deploy the application in server mode.

2) Exclude the routes (see here) that you don't want to be statically pre-rendered. That way, they will be SPA pages that are not pre-rendered, and your middleware code should work fine on these files.

I'd go for solution 2 in your case, since pre-rendering is awesome.