Next.js Discord

Discord Forum

App Router, Server Components, Authentication, help!

Unanswered
Brown bear posted this in #help-forum
Open in Discord
Brown bearOP
Hey all,

I'm not sure if the title is accurately describing my question here, but I'm having some contradicting ideas related to implementing routing using app router, mixed with authentication, while utilizing a client side UI library which must be run in client components.

The background here is that most? client UI libraries utilize react hooks for context providers in order to manage theme, etc. At least what I'm using, https://mantine.dev/, requires any mantine component rendered within Next, to be done within a client component. I'm also using https://authjs.dev/ v5 for authentication.

Now, for example sake, lets say I have these three different pages in my application:
/ (only here to serve as a redirect)
/dashboard
/users
/login

This will be created using the following routing structure:
app
  (authenticated)
    dashboard
      page.tsx
    users
      page.tsx
    layout.tsx
  login
    page.tsx
  layout.tsx
  page.tsx

Let me know if this thought process is incorrect, wrong, or not as efficient as another design:
- Root layout.tsx will be a client component, which wraps the entire application in React Providers.
- Root page.tsx will be a server component, which fetches my current auth session and redirects to either /dashboard or /login depending on the result.
- (authenticated)/layout.tsx will be a server component, which fetches my current auth session, and redirects to /login if the user is logged out. Otherwise, it will render a random arbitrary react component to actually serve as my layout for all the pages within the (authenticated) group. I'm not sure if I'm over thinking this, but this is a major problem that I'm having. I'd like to remove the need for the extra layout component within the "actual" layout:
export default async function AuthenticatedLayout({ children }: { children: React.ReactNode }) {
  const session = await auth();
  if (!session?.user) redirect('/login');
  return <AuthenticatedActualLayout user={session.user}>{children}</AuthenticatedActualLayout>;
}

NextAuth.js < v5 supported useSession(), but on their new v5 docs they suggest that I probably don't need to be using that client side with the new app router (https://authjs.dev/reference/nextjs/react#usesession). So is there a way to use the Auth.js' auth() function as I am in the above code block, but also render <AuthenticatedActualLayout /> within the actual layout.tsx file? It seems very odd and overly complicated that I need to create an additional layer of abstraction because the AuthenticatedActualLayout component renders client side UI components.
- Login page.tsx will be a client component, but that's fine.

Thanks for reading.

9 Replies

American Crow
Thats a lot of questions.

1. Your Root Layout will stay a server component.
You create a helper client component e.g. providers.tsx
'use client';

import { ThemeProvider } from 'acme-theme';
import { MantineProvider } from 'mantine';

export function Providers({ children }) {
  return (
    <ThemeProvider>
      <MantineProvider>{children}</MantineProvider>
    </ThemeProvider>
  );
}


and import that into your Root Layout:
import { Providers } from './providers';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

As mentioned that way your Root Layout remains a server component.

2. Do not rely on Layouts for authorization, they can be bypassed. I'll have to scan the docs of auth v5 again, however if i remember correctly their paradigm is to auth the user in the component (or as close as possible) which should be protected. So do a await auth() within every page that needs it. Combine that with middleware where nextauth runs. Again do not only rely on middleware alone, they say that in the docs as well
Brown bearOP
Yea, more of a general line of questions 😂 - thanks for answering.

1) Think makes sense. That is a similar pattern as I'm describing within the bullet point re: (authenticated)/layout.tsx. This is my first app build using RSC, and the approaches are definitely different. It's interesting to me, it seems like this inherently leads to more, smaller, components to separate the client and server components.

2) If I go this route, then every page then just becomes a small wrapper around the ActualPage, no? Each page will inevitably need to be a client component due to the need for UI lib components. I'll read into this more, but what's the difference between the await auth() within the layout vs the page?
American Crow
For 2) the difference is that you can bypass layouts and still get the contents of a page, even if you auth within that parent layout. I will try to find a resource for this as well. But this was recently discussed in this discord server and i read a blog post where someone showed how it can be done. So i am 99% sure on this.

"Each page will eventually be a client component". I think you have to look at it more modular.
For me the entry point (page.tsx) is almost always a server component. In most cases you do your fetching within that page.tsx. In your case you can do the server side auth within that component. Eventually it will import <ClientComponent1 /> <ClientComponent2 /> and so on. But thats the new paradigm with RSC
Brown bearOP
Yea, it seems like that's the new paradigm I'll just have to adjust to. It's an interesting give and take, as it adds horizontal complexity to the app. This all makes sense though, thanks. If you can find that article I'd take a read!
American Crow
Let me tag a mod @joulev here. he might got the resource that layouts can be bypassed for auth. He was also discussing this recently as mentioned.
I am not finding the original blog post but here is a YT video for now https://youtu.be/kbCzZzXTjuw?t=195 discussing that point
Brown bearOP
Ah, the layout issue with subsequent page redirects makes sense
Brown bearOP
thanks!