#Pinia state leaks across browser sessions

53 messages · Page 1 of 1 (latest)

unique locust
#

I have a pretty severe issue where data stored in Pinia is sometimes leaked across requests. I have a store that is persisted in cookies using the @pinia-plugin-persistedstate package. The state of this is sometimes set across all users so all users have the same pinia data. Even though their cookies are empty, when looking at __NUXT__.pinia the state is set to that of another user.

Any idea's how this can accur?

copper orbit
#

do you use pinia in your plugins?

unique locust
#

I do use it in a plugin that wraps the basic $fetch to always send the authentication headers along with it like follows:

import type { LoginResponse } from '~/utils/types';

export default defineNuxtPlugin(() => {
  const runtimeConfig = useRuntimeConfig();
  const baseURL = runtimeConfig.public.apiBaseUrl || 'localhost:8000/api';

  const $api = $fetch.create({
    baseURL,
    retryStatusCodes: [401],
    retry: 1,
    onRequest({ options }) {
      const authStore = useAuthStore();
      if (authStore.isLoggedIn && authStore.accessToken) {
        options.headers = {
          ...options.headers,
          Authorization: `Bearer ${authStore.accessToken}`,
        };
      }
    },
    onResponseError: ({ response }) => {
      const authStore = useAuthStore();
      if (response.status === 401) {
        return $fetch<LoginResponse>('/auth/refresh', {
          method: 'POST',
          baseURL,
          credentials: 'same-origin',
          headers: {
            'Authorization': `Bearer ${authStore.refreshToken}`,
            'Access-Control-Allow-Origin': '*',
          },
          body: {
            refresh_token: authStore.refreshToken,
          },
        }).then((response) => {
          authStore.signIn(response);
        }).catch(async () => {
          authStore.logout();
          await navigateTo('/login');
        });
      }
    },
  });

  return {
    provide: {
      api: $api,
    },
  };
});

Considering your point, I assume this is not SSR safe than?😅 Is there another way I should call useAuthStore()?

unique locust
#

Would passing the pinia instance to the store as follows fix this issue @copper orbit

export default defineNuxtPlugin((ctx) => {
  const runtimeConfig = useRuntimeConfig();
  const baseURL = runtimeConfig.public.apiBaseUrl || 'localhost:8000/api';
  const $pinia = ctx.$pinia as Pinia;

  const $api = $fetch.create({
    baseURL,
    retryStatusCodes: [401],
    retry: 1,
    onRequest({ options }) {
      const authStore = useAuthStore($pinia);
      if (authStore.isLoggedIn && authStore.accessToken) {
        options.headers = {
          ...options.headers,
          Authorization: `Bearer ${authStore.accessToken}`,
        };
      }
    },
    onResponseError: ({ response }) => {
      const authStore = useAuthStore($pinia);
      if (response.status === 401) {
        return $fetch<LoginResponse>('/auth/refresh', {
          method: 'POST',
          baseURL,
          credentials: 'same-origin',
          headers: {
            'Authorization': `Bearer ${authStore.refreshToken}`,
            'Access-Control-Allow-Origin': '*',
          },
          body: {
            refresh_token: authStore.refreshToken,
          },
        }).then((response) => {
          authStore.signIn(response);
        }).catch(async () => {
          authStore.logout();
          await navigateTo('/login');
        });
      }
    },
  });

  return {
    provide: {
      api: $api,
    },
  };
});
unique locust
#

Thank you so much man!

unique locust
# copper orbit That’s what I’d suggest

We unfortunately encountered the same issue so this didn't seem to be the fix. Do you maybe have any other idea's that could cause this? It's very hard to reproduce this since it only happens once in a while and I am not sure what circumstances cause this to happen.

scenic slate
#

@unique locust Do you define and import your default state from a separate file?

unique locust
#

@scenic slate The store looks as follows:

import { defineStore } from 'pinia';
import type { UserProfile } from '~/utils/types';

export const useAuthStore = defineStore(
  'auth',
  () => {
    const accessToken = ref('');
    const refreshToken = ref('');
    const profile = ref<UserProfile | null>(null);

    const isLoggedIn = computed(() => !!accessToken.value);

    function signIn(login: LoginResponse) {
      accessToken.value = login.access_token;
      refreshToken.value = login.refresh_token;
      profile.value = login.user;
    }

    function logout() {
      accessToken.value = '';
      refreshToken.value = '';
      profile.value = null;
    }

    return {
      accessToken,
      refreshToken,
      profile,
      logout,
      isLoggedIn,
      signIn,
    };
  },
  {
    persist: {
      storage: piniaPluginPersistedstate.cookies(),
    },
  },
);

This is where the default state is set.

analog quest
#

seems like this might be a bug in piniaPluginPersistedstate.cookies

quick knoll
#

FWIW, I use this plugin and haven't seen this behavior of cookie sharing between users.

@unique locust Are you using the Nuxt module? It should be declared in your modules config as '@pinia-plugin-persistedstate/nuxt',

I set the persist config on a store like so:

persist: {
    storage: persistedState.cookiesWithOptions({
      maxAge: 31536000,
    }),
  },
unique locust
unique locust
quick knoll
#

I'm just curious - my config uses persistedstate but yours uses piniaPluginPersistedstate

#

where are you defining piniaPluginPersistedstate ?

unique locust
quick knoll
#

hmmm - I don't actually have that in my package.json - I just use:

"@pinia-plugin-persistedstate/nuxt": "^1.2.1",

#

which is the most recent version of that module

unique locust
#

That is because they moved the module

quick knoll
#

appears to use 4.1.1

unique locust
quick knoll
#

Yeah, odd - the docs seem to use both...

#

well now I am scared to change what's working! 😛

#

I just tested a few different users in different incognito tabs - no pinia state shared

#

so yeah, something must be getting written and persisted to your store in an unusual way

unique locust
#

The thing is, the same for me😅 It happens only occasionally

#

That is what makes it so hard

quick knoll
#

Do you have any routeRules? Where your state is being cached?

unique locust
#

I do use isr on some routes

quick knoll
#

if you are caching state on the instance, its possible that if it's leaking

#

that might be the issue

#

Caching has been tricky, as my app (probably like yours) has lots of static content, but then also a user, with login state and a header with their name/avatar/data

#

so if you use isr or swr, it ends up caching the user state

#

Maybe wrapping all user-specific content in ClientOnly would circumvent that, but I'm not sure

unique locust
#

I think you might be right. But let's say I make the navbar client only (which uses some auth logic) it would jump into the page which is not so nice. I think I'd have to just remove the cache rules and see if that works out.

copper orbit
#

instead of caching the whole SSR'd page

#

also outlined that in https://www.youtube.com/watch?v=Zli-u9kxw0w I think - or one of the other videos 😂

🔗 10$ off for Michael's Nuxt Tips Collection with the following link and code DEJAVUE https://michaelnthiessen.com/nuxt-tips-collection?aff=plY9z *

🔗 10% off for vuejs.de Conf with Code LICHTER https://conf.vuejs.de/tickets/?voucher=LICHTER *


Links and Resources:

🔗 Code TODO
🔗 h3 https://h3.unjs.io/
🔗 unstorage https://unstorage.unjs.io/
...

▶ Play video
quick knoll
#

Like, a global config to set all components as server components

copper orbit
#

I mean, you can do that through .server.vue, also for pages etc.

quick knoll
#

and use the nuxt-client on the ones that are different

#

Right - but I have like 400+ components 🙂

#

My use case is that most of the pages I wanted heavily cached

#

but there are "islands" of user-content that should never be cached

#

and I was concerned about how performant setting ALL the components to .server.vue might be

#

but I may experiment with it and see

unique locust
unique locust
#

1 last thing, I have ISR set to 3600 (so 1 hour). Just for my understanding, under what circumstances will the state be leaked since it's hard for me to reproduce?

copper orbit
unique locust
#

I did confirm that this was indeed the issue😃 Thank you so much!

analog quest