nextauthjs / next-auth

Authentication for the Web.
https://authjs.dev
ISC License
24.93k stars 3.52k forks source link

Next-auth 4 blocks fetch request on RSC even if user is logged in #9232

Open VladyslavL opened 11 months ago

VladyslavL commented 11 months ago

Environment

System: OS: macOS 14.1.1 CPU: (12) arm64 Apple M2 Pro Memory: 181.88 MB / 16.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 20.9.0 - /usr/local/bin/node Yarn: 1.22.19 - /usr/local/bin/yarn npm: 10.1.0 - /usr/local/bin/npm pnpm: 8.9.0 - /usr/local/bin/pnpm Browsers: Chrome: 119.0.6045.159 Safari: 17.1

Reproduction URL

https://github.com/VladyslavL/next-auth-rsc-fetch-bug

Describe the issue

Next-auth 4 blocks a fetch request on the React Server Component but allows it on the React Client Component while a user is authenticated.

How to reproduce

LIVE: https://next-auth-rsc-fetch-bug.vercel.app/1

Login: test Password: test

On the top of the page, you can see the fetch response on RSC and the response on RCC below

Expected behavior

Fetch request shouldn't be blocked on RSC if user is authenticated.

tills13 commented 11 months ago

I don't think this is necessarily a next-auth bug nor even incorrect behaviour. Your internal fetch is a separate subrequest back to your app. That subrequest has its own headers, credentials etc.

You probably don't want all headers from the parent request, but you can change the line

const res = await fetch(
    `${process.env.APP_URL}/api/v1/posts/${params.payment_id}`
  );

in [payment_id]/page.tsx to

const res = await fetch(
    `${process.env.APP_URL}/api/v1/posts/${params.payment_id}`,
    { headers: headers() }
  );

(headers() is from next/headers). This will copy headers (containing session cookies from the parent request into the subrequest.

tills13 commented 11 months ago

BTW I made the assumption that process.env.APP_URL was the external URL of the app e.g. https://next-auth-rsc-fetch-bug.vercel.app in your example. Let me know if that assumption is incorrect.

VladyslavL commented 11 months ago

@tills13 Thanks for your reply, first of all!

Please look at thenext.config.js file, here is the rewrites() configured. Sorry, I forgot to mention that in the post. So, nextjs API is just proxying all requests from /api/v1/ to external API. Currently used EXTERNAL_API_URL=https://jsonplaceholder.org

Both requests are configured in this way. It works perfectly on the client component but not on the server.

It works without next-auth, or without proxying, e.g., with direct URL - https://jsonplaceholder.org/posts/1

tills13 commented 11 months ago

Did you see my first comment? I was able to figure out the next.config.js redirects.

VladyslavL commented 11 months ago

Thanks, @tills13. It works like a charm, but I still don't understand why you called a server component request as a subrequest and why next-auth works differently in client and server components. Can you help me with that?

tills13 commented 11 months ago

why you called a server component request as a subrequest

The parent request is from your computer to the server where NextJS reads your request and starts executing it. As part of that, any data fetching using fetch is a subrequest -- it still might be to the local app, but it's got a unique set of headers and cookies since it did NOT originate from your browser with your cookie jar and credentials.

why next-auth works differently in client and server components

Mostly for the reasons above. In a client component, calling fetch automatically has access to your browser credentials and cookies. In a server component, because it's a completely separate request from your original request, it has no credentials or cookies unless otherwise specified.

pygaurav commented 10 months ago

@tills13 You saved my day 👍