awinogrodzki / next-firebase-auth-edge

Next.js Firebase Authentication for Edge and Node.js runtimes. Compatible with latest Next.js features.
https://next-firebase-auth-edge-docs.vercel.app/
MIT License
499 stars 43 forks source link

Current example forces dynamic rendering on all pages #20

Closed steve-marmalade closed 1 year ago

steve-marmalade commented 1 year ago

Hey there, apologies if this was self-evident to others but caught me by surprise: the current example uses cookies() in the root layout, which is considered a dynamic function (since the contents will only be known at request-time). As a result, it is not possible to statically generate any of the pages in the app, regardless of whether those pages use the auth context.

As a workaround, I am planning to use the cookies only in the middleware, and then let client components separately handle authentication using the firebase SDK. Would be nice to avoid the network request to firebase and leverage the cookie, but I can't give up static generation.

Figured I'd share in case it was useful. Thanks again for the effort put into this library.

awinogrodzki commented 1 year ago

Hey @steve-marmalade,

You've got a serious point here.

Basically, any page that depends on user cookies should be considered dynamic and cannot be statically generated.

My solution here would be to remove AuthProvider and cookies() from root layout altogether and include it only on pages that are supposed to be dynamic and have content exclusive to specific user.

You've given me an idea of providing example with statically generated pages. I'll add it as a resolution to this issue.

Thanks for feedback!

steve-marmalade commented 1 year ago

Awesome, glad it was helpful!

If you are looking for inspiration - I think a powerful (and challenging) use-case would be one where the page is static, except for e.g. a component in the Navbar that contains a user's profile icon. I am not sure if it's possible to have only certain components on a page be dynamic, or if the use of dynamic functions in any component on the page will case the entire page to be dynamic.

Currently I am planning to use the Firebase Javascript SDK in that case, so that the page immediately loads the static content and then is progressively enhanced with user data in a client component, but it introduces the risk that there are edge cases where the authentication state in the cookies and in the client get out of sync.

awinogrodzki commented 1 year ago

Hey @steve-marmalade,

I did experiment a while with static generation and come up with next13-typescript-static-pages example.

You can test it with a Google sign up method at next-firebase-auth-edge-static-demo

Basically, there are three options when it comes to authenticating pages in Next.js 13:

  1. No authentication – a static page with no calls to dynamic cookies function as in here
  2. Authentication with cookies in layout (either root or nested), as in /login page layout example
  3. Authenticating with cookies directly in page.tsx or any nested server component, as seen on the profile page

Although 1. supports static page generation, 2. and 3. will force server-side rendering because of calling cookies() upon rendering

Unfortunately, Next.js does not seem to support partial static generation. If your server components want to access request context at some point, they have to be rendered dynamically.

You can still achieve user-dedicated content on static pages without accessing cookies, by rendering user components using client-side authentication.

Let me know if this answers your question. Cheers!

steve-marmalade commented 1 year ago

@awinogrodzki - thank you for the thoughtful responses and examples.

Regarding your point:

Unfortunately, Next.js does not seem to support partial static generation. If your server components want to access request context at some point, they have to be rendered dynamically.

I wonder whether you think the new Vercel Cache API changes this at all? Here's an excerpt from the intro:

Before today, developers had to choose between either fully static or fully dynamic pages.

With Next.js 13.2, we’re excited to announce the Next.js Cache (beta) and the brand-new Vercel Cache API (beta). This enables caching only part of your page as static data, while fully dynamically rendering the rest of your application, including accessing real-time and personalized data.

awinogrodzki commented 1 year ago

@steve-marmalade, thanks for the article, it's pretty interesting. I might have even found a reason for a bug I've been experiencing recently.

Key takeaway from the article is this:

    // Cached until manually invalidated
    fetch(`https://...`),
    // Refetched on every request
    fetch(`https://...`, { cache: 'no-store' }),
    // Cached with a lifetime of 10 seconds
    fetch(`https://...`, { next: { revalidate: 10 } }),

If we don't pass any headers to fetch, it will be cached indefinitely.

To understand how next-firebase-auth-edge is using fetch, let's look at this flow chart:

test

The actual http fetch call does not happen until the last step: refresh and return new token. The refresh token step is called with specific Authorization headers, which mean it is not cached (thankfully).

This means two things. We cannot really leverage Next Cache in a meaningful way, but we don't really need to. Token verification step is instant as soon as provided idToken is valid.

I also want to thank you. When investigating this topic I have found out, that we don't overwrite stale cookies with new ones – this means that as soon as original idToken is expired, we will make request to refresh the token each time user access the page. I will fix that today.

Cheers!