nuxt / nuxt

The Intuitive Vue Framework.
https://nuxt.com
MIT License
54.95k stars 5.03k forks source link

Middleware + pinia not working #15446

Closed maksimdrosdov closed 1 year ago

maksimdrosdov commented 2 years ago

Environment

middleware

Reproduction

middleware

Describe the bug

When I reload the page, it throws me to the authorization page, although the user is authorized

import { useAuthStore } from '~/store/auth';

export default defineNuxtRouteMiddleware(async () => {
  const { loggedIn } = useAuthStore();

  if (!loggedIn) {
    return navigateTo('/auth');
  }
});

Additional context

No response

Logs

No response

HigherOrderLogic commented 2 years ago

This doesn't look like a Nuxt bug. Can you provide a minimum reproduction?

Dodje commented 2 years ago

I believe that happens because you need to connect your pinia instance with app store.

In your vue components pinia can get store context from app's context (this.$store) via some magic, but there is no such a thing in a simple general function. But you can get it from ctx.app.$store Sadly I've not worked with pinia closely, but I guess a little trick from Vuex-smart-modue might help you to find easy solution, You need to connect you store manager with store context manually:

// vuex-smart-module example
// Get module context
const ctx = yourModule.context(store);

So if there is no such a thing for pinia, there is an issue in the pinia lib.

And actually we can find similar issue there

maksimdrosdov commented 2 years ago

console.log('loggedIn = ', loggedIn);

shows that true, but everything exactly redirects to auth

maksimdrosdov commented 2 years ago

@danielroe sent an invite

manniL commented 2 years ago

@maksimdrosdov Please be aware that a full-blown project is no reproduction 🙈

The goal is to have as little "moving pieces" as possible and a project that is as small as possible to reproduce the bug so contributors can pin it down easily and it is not caused by any custom userland logic

Dodje commented 2 years ago

console.log('loggedIn = ', loggedIn);

shows that true, but everything exactly redirects to auth

There might be a little trick with lifecycle and browser's logs..

Try to do smth like this to clear link to store's object

const authState = useAuthStore();
console.log({...authState});

And check your loggedIn in exact moment

maksimdrosdov commented 2 years ago
Снимок экрана 2022-11-14 в 23 54 24
import { storeToRefs } from 'pinia';
import { useAuthStore } from '~/store/auth';

export default defineNuxtRouteMiddleware(() => {
  const authStore = useAuthStore();

  console.log({...authStore});

  const { loggedIn } = storeToRefs(authStore);

 if (!loggedIn.value) {
  //return navigateTo('/auth');
 }
});
typhoon11 commented 1 year ago

This problem is still there please fix it. In middleware pinia data is empty

manniL commented 1 year ago

@typhoon11 Can you provide a reproduction?

typhoon11 commented 1 year ago

@typhoon11 Can you provide a reproduction?

So I made a getter in pinia image and tried them on middleware and a page image

So it's visible that pinia is empty for the files in the middleware directory

manniL commented 1 year ago

@typhoon11 Please provide a repository or use Stackblitz or Codesandbox as linked above. This is the best way to make it easy for maintainers to help you. I highly recommend to read the linked paragraph above.

typhoon11 commented 1 year ago

@typhoon11 Please provide a repository or use Stackblitz or Codesandbox as linked above. This is the best way to make it easy for maintainers to help you. I highly recommend to read the linked paragraph above.

Sorry for late reply. I tested it and found it works on normal page redirect but when we refresh the page it becomes unavailable. I attach the stack blitz below https://stackblitz.com/edit/github-nf9a2f

manniL commented 1 year ago

@typhoon11 In your middleware, you should not trigger navigateTo in the "default"/"happy" case. Instead, just not return anything and the middleware will "succeed".

import { useDiscordUser } from '~/stores/auth';

export default defineNuxtRouteMiddleware(async (to, from) => {
  const app = useNuxtApp();
  const user = await useDiscordUser(app.$pinia);

  if (!user.user) {
    console.log('User not logged in');
    return navigateTo('/');
  }
});
typhoon11 commented 1 year ago

@typhoon11 In your middleware, you should not trigger navigateTo in the "default"/"happy" case. Instead, just not return anything and the middleware will "succeed".

import { useDiscordUser } from '~/stores/auth';

export default defineNuxtRouteMiddleware(async (to, from) => {
  const app = useNuxtApp();
  const user = await useDiscordUser(app.$pinia);

  if (!user.user) {
    console.log('User not logged in');
    return navigateTo('/');
  }
});

In my actual code if the user is not logged in I have to send the user to an external api for login so I need navigateTo for that

manniL commented 1 year ago

@typhoon11 Yes, that's fine. But before, you called navigateTo in both cases, if the user exists and not. in the default case (logged-in), you should not ☺️

typhoon11 commented 1 year ago

@typhoon11 Yes, that's fine. But before, you called navigateTo in both cases, if the user exists and not. in the default case (logged-in), you should not ☺️

Nope it didn't fixed on doing refresh the data becomes null my code: image

manniL commented 1 year ago

You don't even need the else and return there.

Be aware that the middleware is executed on server on the first request (and no window will be available there)

typhoon11 commented 1 year ago

You don't even need the else and return there.

Be aware that the middleware is executed on server on the first request (and no window will be available there)

Even if I remove the else it's the same, like when I am doing page navigation the pinia data is there and it allows me to open but whenever I press refresh on that page the data is gone

typhoon11 commented 1 year ago

You don't even need the else and return there.

Be aware that the middleware is executed on server on the first request (and no window will be available there)

I am guessing the pinia code I have written is wrong, can you help me with that?

export const useDiscordUser = defineStore('discord_user', {
    state: () => {
        return {
            user: null as UserPartial | null
        }
    }, 
    getters: {
        isUserLoaded: (state) => {
            return state.user !== null
        }
    }
})
manniL commented 1 year ago

Doesn't look wrong to me but depends how you fetch the user. I've adapted your example. When you switch the user as null/actual user, you see the middleware works as expected

typhoon11 commented 1 year ago

Doesn't look wrong to me but depends how you fetch the user. I've adapted your example. When you switch the user as null/actual user, you see the middleware works as expected

I am fetching the user details from an external API and that's done in the header component, so when the page loads and the middleware gets initiated the user value becomes null, so I had to save the user details as local storage and keep the pinia data hydrated.

manniL commented 1 year ago

I am fetching the user details from an external API and that's done in the header component, so when the page loads and the middleware gets initiated the user value becomes null, so I had to save the user details as local storage and keep the pinia data hydrated.

The server has no access to localstorage as mentioned above. Instead, you could e.g. use a cookie or do the request in the middleware instead?

alouimohamedhabib commented 1 year ago

I allow my self to re-open this ticket since the problem has not been solved yet!

manniL commented 1 year ago

@alouimohamedhabib instead, open a new issue with a reproduction! Thanks ☺️

InochiSoft commented 1 year ago
import { useAuthStore } from '~/stores'

export default defineNuxtRouteMiddleware((to, from) => {
  const authStore = useAuthStore()
  const authenticated = computed(() => authStore.isAuthenticated())
  if (!authenticated) {
    return navigateTo('/login')
  }
})
robextrem commented 10 months ago

Had the same issue, and solved with @InochiSoft solution but adding .value

import { useUserStore } from "~/stores/modules/user";

export default defineNuxtRouteMiddleware((to, from) => {
    const userStore = useUserStore();
    const authenticated = computed(() => userStore.isAuthenticated())
    if (!authenticated.value) {
        return navigateTo('/login');
    }
});

I hope someone finds it useful

aynakeya commented 9 months ago

try chck if process.server is true

some like this

export const requireAuth =  defineNuxtRouteMiddleware((to, from) => {
    const userStore = useUserStore();
    // since userStore only exist in client. ignore server side
    if (process.server) {
        return
    }
    if (userStore.isLogin) {
        return
    }
    if (to.path !== "/admin/login") {
        return navigateTo('/admin/login')
    }
})
marianharnageaq commented 8 months ago

found a solution. might help

added userStore.$subscribe((cb) => cb.events)

import { useUserStore } from '~/stores/userStore';

export default defineNuxtRouteMiddleware(() => {
    const userStore = useUserStore();

    userStore.$subscribe((cb) => cb.events)
    const isAdmin = computed(() => userStore.isAdmin)

    if (!isAdmin.value) {
        return navigateTo('/');
    }
});
KonstantinDyulin commented 6 months ago

@marianharnageaq, This will be checked every time the pinia state changes. If, for example, you change the user’s name, he will be immediately redirected to the Index page

kiketordera commented 15 hours ago

Same problem here.

The middleware with firebase (or vuefire example)

For example, in this example where if implemented, redirects the user to the login page if not logged in:

// middleware/auth.ts
export default defineNuxtRouteMiddleware(async (to, from) => {
  const user = await getCurrentUser()

  // redirect the user to the login page
  if (!user) {
    return navigateTo({
      path: '/login',
      query: {
        redirect: to.fullPath,
      },
    })
  }
})

will work navigating normally, but won't work if you reload the page from the browser, it will asume there is no user logged in even if there is, and it's because it does not wait firebase to log the user, so is null or undefined and fails and redirects to login page even tho there is an user logged in