nuxt-community / firebase-module

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

[SSR][Auth] onAuthStateChanged is delayed for 2 seconds, causing the login button to briefly appear again. #502

Open ErwinAI opened 3 years ago

ErwinAI commented 3 years ago

I've implemented SSR+Auth by following the tutorial. After that, I've modified it to use Google Sign-in instead (with some help from this article).

Very happy with those resources, documentation and this module! Thank you for building this!

Unfortunately, I've encountered a problem that is beyond my ability to solve alone: after logging in with Google, I return to the same route. The login button is still visible for a brief moment (about two seconds) after which onAuthStateChanged triggers and the proper user information is displayed.

I receive the following error in my console:

[Vue warn]: The client-side rendered virtual DOM tree is not matching server-rendered content. This is likely caused by incorrect HTML markup, for example nesting block-level elements inside <p>, or missing <tbody>. Bailing hydration and performing full client-side render.

Auth component

<template>
  <div>
    <div v-if="!this.isLoggedIn">
      <button @click="signInUser()">Login</button>
    </div>
    <div v-else>
       <img :src="this.$store.state.authUser.photoURL" :alt="this.$store.state.authUser.displayName">
       <p>{{ this.$store.state.authUser.email }}</p>
       <button @click="logout()">Logout</button>
    </div>
  </div>
</template>

<script>
import { mapState, mapGetters } from 'vuex'

export default {
  name: "Auth",
  computed: {
    ...mapState({
      authUser: state => state.authUser
    }),
    ...mapGetters({
      isLoggedIn: 'isLoggedIn',
    }),
  },
  methods: {
    async signInUser() {
      try {

        let provider = new this.$fireModule.auth.GoogleAuthProvider();
        await this.$fire.auth.signInWithRedirect(provider);

      } catch (e) {
        alert(e)
      }
    },
    async logout() {
      try {
        await this.$fire.auth.signOut()
      } catch (e) {
        alert(e)
      }
    },
  },
}
</script>

nuxt.config.js:

....
ssr: true,
firebase: {
    config: {
      ...
    },
    services: {
      auth: {
        ssr: true,
        initialize: {
          onAuthStateChangedAction: 'onAuthStateChanged',
        },
      },
    }
  }
...
tibs245 commented 3 years ago

I have not use external provider for sign in.

But I have read your problem and show $fire.auth functions.

getredirectresult can may be respond to your problem ?

https://firebase.google.com/docs/reference/js/firebase.auth.Auth#getredirectresult

I hope this function is relevant to your problem

dosstx commented 3 years ago

FYI, the official google firebase auth examples (not Nuxt related) have the same issue.

See this: https://github.com/firebase/firebaseui-web/issues/436

If you use the other Google sign in SDK, this won't be an issue....but thats not something I want to have to do.

lupas commented 3 years ago

Hey @ErwinAI

Sorry for the late response - Is this still an issue?

ErwinAI commented 3 years ago

@lupas Yes this is still an issue. I am currently using .signInWithPopup(provider) which does not have this delay. Ideally, I'd still want to use .signInWithRedirect(provider) for mobile users for the sake of improved usability.

dosstx commented 3 years ago

I also get the same issue and the only solution at the moment is to use the Popup method, but as mentioned above, it's not recommended for mobile users, thus I don't use that solution.

There is chatter on firebase github about this issue and there does not seem to be a workaround last time I checked. It doesn't seem to be related to Nuxt/Vue because the same issue happens when not using this Nuxt/firebase module.

The other option is to authenticate with Firebase using a Google Account by handling the sign-in flow with the Google Sign-In SDK: https://firebase.google.com/docs/auth/web/google-signin#expandable-2

I have not yet tried the above solution as it is a bit involved and I'm willing to give up the 1-2sec redirection delay for now.

I filed this issue in my backlog...hope someone can help figure this out so I can move it to the "done" column :)

lupas commented 3 years ago

Hey @ErwinAI

Do you get the issue ONLY in the redirect? Or also when the user is logged in and you refresh the page?

If you hit the same issue, you might need to check Step 3 in the SSR documentation here if you set up everything correctly.

If you do the additional dispatch('onAuthStateChanged', { ... }) on server side, the server should be authenticated and you shouldn't get your error.

Let me know if that fixes your problem or if it is really a problem with the redirect only - then I would need to checkit out in more detail.

br, Pascal

florianthiel commented 2 years ago

I have the same issue. The problem is that res.locals is empty on the server:

async nuxtServerInit({ dispatch, commit }, { res }) {
    // res.locals is always empty on the server
    if (res && res.locals && res.locals.user) {
      const { allClaims: claims, idToken: token, ...authUser } = res.locals.user
      commit('auth/ON_AUTH_STATE_CHANGED_MUTATION', { authUser, claims, token })
      await dispatch('auth/onAuthStateChangedAction', { authUser, claims, token })
    }
  }
calebebteixeira commented 2 years ago

Yeah res.locals is always empty... just cloned and initialized https://github.com/lupas/nuxt-firebase-demo Screenshot_2021-11-14-10-24-43_1920x1080

LeeGrobler commented 2 years ago

@lupas

Do you get the issue ONLY in the redirect? Or also when the user is logged in and you refresh the page?

I've got the exact same setup as OP and in my case the issue only occurs on the initial redirect back from google sign in. So, I click Sign in with Google, it redirects me to a google page to sign in, then it brings me back to my application where res.locals is empty and it throws the client-side ... not matching server-rendered content error, like OP said. But if I then refresh the page res.locals is no longer empty and everything works like it should.

My thought was to use firebase's .getRedirectResult() in a middleware but that method can only be used client-side and so it throws an error, so that doesn't seem to be the case. Maybe if we could add it to firebase-auth-sw.js ..? I dunno how to service-worker yet though so please advise if that'd be a good idea and how we might do it?

grimzy commented 2 years ago

Can confirm that this issue also occurs when running the demo: https://github.com/nuxt-community/firebase-module/tree/master/packages/demo. Running the demo locally with the Firebase emulators produces the same errors.

If you do the additional dispatch('onAuthStateChanged', { ... }) on server side, the server should be authenticated and you shouldn't get your error.

@lupas could you elaborate how to implement this? When following the documentation, res.locals.user is seems to always be empty server-side when refreshing a page while logged in.


PS: setting ssr: false and firebase.services.auth.ssr: false in the Nuxt configuration clears the error/warnings.