Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why is the SessionId always different when you send requests via useFetch? #27683

Open
yesworld opened this issue Jun 18, 2024 · 7 comments
Open

Comments

@yesworld
Copy link

yesworld commented Jun 18, 2024

Environment

Nuxt project info: (copied to clipboard) 11:00:46 AM


  • Operating System: Darwin
  • Node Version: v20.11.1
  • Nuxt Version: 3.12.2
  • CLI Version: 3.12.0
  • Nitro Version: 2.9.6
  • Package Manager: pnpm@8.15.4
  • Builder: -
  • User Config: vite, devtools, css, modules, $development, srcDir, serverDir, colorMode, app, runtimeConfig, components
  • Runtime Modules: @nuxt/image@1.7.0, @nuxt/eslint@0.3.13, @nuxtjs/tailwindcss@6.12.0, @nuxtjs/color-mode@3.4.1, nuxt-icon@0.6.10, @vueuse/nuxt@10.11.0, @nuxtjs/device@3.1.1, vue3-carousel-nuxt@1.1.1
  • Build Modules: -

Reproduction

https://stackblitz.com/edit/github-mpxmop?file=app.vue,server%2Fapi%2Ftest%2Findex.ts

app.vue

<script setup>
const { data } = await useFetch(`/api/test`, {
  // credentials: 'include',
});
</script>
<template>
  <div>
    <p>
      If you simply refresh the page, there will be a new session every time.
    </p>
    <pre>{{ data }}</pre>

    <p>
      But if you go straight to endpoint:
      <a href="/api/test">/api/test</a>, the session id will be remain
      permanent. Just what I need!
    </p>
  </div>
</template>

/api/test/index.ts

export default defineEventHandler(async (event) => {
  const session = await getSession(event, {
    password: '80d42cfb-1cd2-462c-8f17-e3237d9027e9',
    name: 'seesion-id-test',
  });

  return {
    sessionId: session.id,
  };
});

Describe the bug

Hello Nuxt 3 team!

I apologize if this is not a bug, but my stupidity! :) I don't understand how I work in h3 with nuxt3.

If I send req through useFetch(), I get a different sessionId each time, but if I go directly to endpoint the session is fixed permanently sessionId. How can I get the same sessionId on the server side all the time if useFetch is going to be called all the time?

Cookies with a name "seesion-id-test" and a hash sometimes appear, sometimes they don’t.

Thank you for your attention!

Additional context

No response

Logs

No response

Copy link

stackblitz bot commented Jun 18, 2024

Fix this issue in StackBlitz Codeflow Start a new pull request in StackBlitz Codeflow.

@danielroe
Copy link
Member

You would need to set the cookie returned by useFetch on the HTML response. This isn't done automatically right now, but we might consider doing it for useFetch.

https://nuxt.com/docs/getting-started/data-fetching#pass-cookies-from-server-side-api-calls-on-ssr-response

@yesworld
Copy link
Author

@danielroe thanks for the answer!

But that doesn't work for me either. I created a composable fetchWithCookie and used it in my code. The sessionId is different every time.

<script setup>
const event = useRequestEvent();

const { data } = await useAsyncData(() => fetchWithCookie(event, '/api/test'));
</script>
<template>
  <div>
    <p>
      If you simply refresh the page, there will be a new session every time.
    </p>
    <pre>{{ data }}</pre>

    <p>
      But if you go straight to endpoint:
      <a href="/api/test">/api/test</a>, the session id will be remain
      permanent. Just what I need!
    </p>
  </div>
</template>

@yesworld
Copy link
Author

yesworld commented Jun 19, 2024

I solved my issue via middleware. And it’s strange, it doesn’t work in the endpoint, but works in the middleware. How can it be?

/server/middleware/session.ts

export default defineEventHandler(async (event) => {
  console.log('middleware', {
    sessionId: event.context.sessionsId,
    cookie: getCookie(event, 'seesion-id-test-2'),
  })

  if (event.context.sessionsId) {
    return
  }

  const session = await getSession(event, {
    password: '80d42cfb-1cd2-462c-8f17-e3237d9027e9',
    name: 'seesion-id-test-2',
    cookie: {
      maxAge: 3600,
      path: '/',
      sameSite: true,
    },
  })

  event.context.sessionsId = session.id
})

@leofredy
Copy link

every stranger how it work, with axios onRequest and onResponse great fine, or am I using it wrong?

@yesworld
Copy link
Author

yesworld commented Jun 27, 2024

@leofredy
can you provide an example?

@danielroe danielroe removed the 3.x label Jun 30, 2024
@Moonlight63
Copy link

I am also experiencing this problem. I am building a module for authentication, and need to use the session ID for caching. I'll see if I can show flow here.

Module plugin:

export default defineNuxtPlugin(async (nuxtApp) => {
    const runtimeConfig = useRuntimeConfig().public.auth

    const { getSession } = useAuth()
    // Skip auth if we're prerendering
    let nitroPrerender = false
    if (nuxtApp.ssrContext) {
        nitroPrerender = getHeader(nuxtApp.ssrContext.event, 'x-nitro-prerender') !== undefined
    }
    if (typeof data.value === 'undefined' && !nitroPrerender) {
        if (import.meta.server) {
            await getSession() 
        }
    }
}

useAuth get session function:

        const headers = await useRequestHeaders(['cookie']) //<-- This doesn't work because "A composable that requires access to the Nuxt instance was called outside of a plugin, Nuxt hook, Nuxt middleware, or Vue setup function."
        console.log("🚀 ~ getSession ~ headers:", headers)
        const response = await $fetch<SessionData>(path, {
            headers: {
                ...headers,
                Accept: 'text/json',
            },
            retry: false,
        }).catch(() => ({}))

useAuth get session function:

        const nuxt = useNuxtApp()
        const headers = await callWithNuxt(nuxt, () => useRequestHeaders(['cookie'])) //<-- this 'works' but still changes the id
        console.log("🚀 ~ getSession ~ headers:", headers)
        const response = await $fetch<SessionData>(path, {
            headers: {
                ...headers,
                Accept: 'text/json',
            },
            retry: false,
        }).catch(() => ({}))

Example snippet from my auth redirect endpoint:

export default defineEventHandler(async (event) => {
    const body = await readBody(event)
    if (!body || !body.state) {
        throw createError({ status: 404, message: 'response not found' })
    }
    const session = await useSession<SessionSignInRequest>(event, {
        password: useRuntimeConfig().session.password,
    })
//... do sign in things ...
    await session.update({
            account: tokenResponse.account || undefined,
            testSessionId: session.id, // <-- lets assume session.id = 1234
    })

example get session endpoint:

export default eventHandler(async (event) => {
  try {
    const session = await getUserPublicData(event)
    return session
  }
  catch (error) {
    return {}
  }
})

export async function getUserPublicData(event: H3Event) {
    const session = await useSession<SessionSignInRequest>(event, {
        password: useRuntimeConfig().session.password,
    })
    console.log(session.id) //<--- From plugin calling getSession() would be 5678, but from middleware calling getSession would be 1234
    consol.log(session.data.testSessionId) // <-- will be 1234
}

Its very frustrating because it basically means I have to store the "correct" session ID along side the data, which isn't the end of the world, but something definitely isn't working right. I am going to try and build a demo repo to show the problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants