Firebase auth coming as null in nuxtjs middleware on page reload

Viewed 441

New to nuxtjs. I'm using Nuxt firebase(https://firebase.nuxtjs.org/) library for firebase. On successfull login I'm redirecting user to "/member/desk" route. But, once redirecting to that particular page, if I refresh the page then I'm redirecting to "/auth/signin" page. It's because in "middleware/auth.js" file I'm not getting app.$fire.auth.currentUser value as null.

I Google this but didn't any reliable info which can fix my issue. Need some help.

Here are my files..

  • nuxt.config.js
[
    '@nuxtjs/firebase',
    {
        config: {
            apiKey: "",
            authDomain: "",
            projectId: "",
            storageBucket: "",
            messagingSenderId: "",
            appId: "",
            measurementId: ""
        },
        services: {
            auth: {
            persistence: 'local', // default
            initialize: {
                onAuthStateChangedAction: 'onAuthStateChangedAction',
                subscribeManually: false
            },
            ssr: false
            }
        }
    }
]
  • store/index.js
const actions = {
    async onAuthStateChangedAction(state, { authUser, claims }) {
        if (!authUser) {
            // remove state
            state.commit('SET_USER', null)

            //redirect from here
            this.$router.push({
                path: '/auth/signin',
            })
        } else {
            // console.log('authUser', authUser);
            const { uid, email } = authUser
            state.commit('SET_USER', {
                uid,
                email,
            })
        }
    },
}

const mutations = {
    SET_USER(state, user) {
        state.user = user
    },
}

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

const getters = {
    getUser(state) {
        return state.user
    },
}

export default {
    state,
    actions,
    mutations,
    getters,
}

  • middleware/auth.js
export default function ({ store, app, route, redirect }) {
    if (route.path === '/') {
        //leave them on the sign in page
    } else if (route.path !== '/auth/signin') {
        //we are on a protected route
        console.log('app.$fire.auth', app.$fire.auth.currentUser);
        if (!app.$fire.auth.currentUser) {
            //take them to sign in page
            return redirect('/auth/signin')
            // return redirect('/auth/signout')
        }
    } else if (route.path === '/auth/signin') {
        if (!app.$fire.auth.currentUser) {
            //leave them on the sign in page
        } else {
            return redirect('/')
        }
    }
}

  • pages/index.js
<template>
  <v-row justify="center" align="center">
    <v-col cols="12" sm="8" md="4">
      <v-form ref="form" v-model="valid" lazy-validation>
        <v-text-field
          v-model="email"
          :rules="emailRules"
          type="email"
          label="E-mail"
          autocomplete="username"
          required
        ></v-text-field>

        <v-text-field
          v-model="password"
          :rules="passwordRules"
          type="password"
          label="Password"
          autocomplete="current-password"
          required
        ></v-text-field>

        <v-btn :disabled="!valid" color="success" class="mr-4" @click="login">
          Login
        </v-btn>
      </v-form>
    </v-col>
  </v-row>
</template>

<script>
export default {
  layout: "plain",
  data: () => ({
    valid: true,
    name: "",
    nameRules: [
      (v) => !!v || "Name is required",
      (v) => (v && v.length <= 10) || "Name must be less than 10 characters",
    ],
    password: "",
    passwordRules: [
      (v) => !!v || "Password is required",
      (v) => (v && v.length >= 6) || "Password must minimums 6 characters",
    ],
    email: "",
    emailRules: [
      (v) => !!v || "E-mail is required",
      (v) => /.+@.+\..+/.test(v) || "E-mail must be valid",
    ],
    select: null,
    items: ["Item 1", "Item 2", "Item 3", "Item 4"],
    checkbox: false,
  }),

  methods: {
    login() {
      let formValidation = this.$refs.form.validate();
      console.log("formValidation", formValidation);

      if (formValidation) {
        this.$fire.auth
          .signInWithEmailAndPassword(this.email, this.password)
          .catch(function (error) {
            // that.snackbarText = error.message;
            // that.snackbar = true;
            console.log("login error", error.message);
            $nuxt.$router.push("/");
          })
          .then((user) => {
            // this.$fire.auth.onAuthStateChanged((user) => {
            //   currentUser = user.uid;
            // });
            //we are signed in
            $nuxt.$router.push("/member/desk");
          });
      }
    },
    reset() {
      this.$refs.form.reset();
    },
    resetValidation() {
      this.$refs.form.resetValidation();
    },
  },
};
</script>

user variable also coming as null

--- UPDATE ---

After implementing "Firebase Auth with SSR(https://firebase.nuxtjs.org/tutorials/ssr#step-3---retrieve-the-server-user)" I managed to fix this to some extent. Now, when I refresh(normal reload) "/member/desk" route it's now working fine(staying at same page). But, when I'm doing HARD RELOAD it's redirecting to "/auth/signin" route.

I believe it's something related to that "Server Worker" implementation. On HARD RELOAD all cached data getting deleted & Service Worker restarted once again.

I maybe wrong also. Can anyone please give me some clarity!!!

1 Answers

As said in firebase auth documentation here (https://firebase.google.com/docs/auth/web/manage-users) :

firebase.auth().currentUser can also be null because the auth object has not completed initialization. If you use an observer to track the user's login status, you don't need to handle this case.

Which means that if your middleware is called too soon - when the auth.onStateChanged is not done yet - app.$fire.auth.currentUser will initially be null.

I suggest you to change your middleware by using the user stored in your Vuex.

export default function ({ store, app, route, redirect }) {
    const user = store.state.user;
    if (route.path === '/') {
        //leave them on the sign in page
    } else if (route.path !== '/auth/signin') {
        //we are on a protected route
        if (!user) {
            //take them to sign in page
            return redirect('/auth/signin')
            // return redirect('/auth/signout')
        }
    } else if (route.path === '/auth/signin') {
        if (!user) {
            //leave them on the sign in page
        } else {
            return redirect('/')
        }
    }
}
Related