NextJS App Directory SSR issue.
a
Hello, I have been having trouble with getting Next.JS App Directory SSR to work properly. I've been following the documentation on https://supertokens.com/docs/thirdparty/nextjs/app-directory/protecting-route#sessions-with-server-components--pre and looked at the repository (https://github.com/supertokens/next.js/tree/canary/examples/with-supertokens/app). (I last checked these pages and followed them today, 2/9/2024) The components sessionAuthForNextJS.tsx and tryRefreshClientComponent.tsx are the same and I followed the code stated in home.tsx. Looking at my configuration files, they seem to be correct. When I attempt to log in, it works just fine and such. Then if I try to go to my dashboard, which has the SSR authentication requirement, I will first be redirected to the auth page for a moment then pushed to the home page '/'. Then every other attempt of trying to go to the dashboard will just continue to redirect me to the home page '/'. If convert the SSR pages into their client side version then it works just fine, however I want to use SSR for increased performance and user experience. I then got confused with the middleware, thinking it was part of the SSR before realizing it was only for APIs, but the middleware approach did fix my issues, until expiration occurred and I'd get a "Authentication required" message. As a temporary fix to this, the user would need to go back to the home page and then they'd be able to go to the dashboard and its sub pages. This sort of works but there are pages where users would go straight to a sub page within the dashboard, so they'd get this message often. Figuring out that the middleware was only for APIs, I went back and commented it out. I am now at square one with SSR authentication again and can't seem to figure out why I am only redirected to the home page and never allowed into my dashboard.
I do have a testing site, let's call it sub.domain.etx, then my production site, domain.etx. I already went to the frontend configuration and added
sessionTokenFrontendDomain: ".domain.etx"
to my session initialization. I know my connectionURI on the backend configuration is correct with the correct API key. I do have a custom supabase db, which my backend config is properly configured to work with. My Next.JS API Folder Layout:
/app/api/auth/[...path]/route.ts
My Next.JS App Dir Auth Folder
/app/auth
My app info:
Copy code
js
export const appInfo = {
  appName: 'ApplicationName',
  apiDomain: "https://sub.domain.etx",
  websiteDomain: "https://sub.domain.etx",
  apiBasePath: "/api/auth",
  websiteBasePath: "/auth",
}
r
Hey @a_tree.
When the access token has expired and you navigate to a page, does the request have the access token cookie in it? Also, can I see the code for how you are handling sessions during SSR?
a
If you are referring to how SSR was originally meant to function according to the documentation, it looks as if the
cookies().getAll()
function does not have any cookies, which I now realize is my issue. I was sure Next.JS automatically set server pages to be dynamically rendered whenever I implement the cookies import and it should've shown cookies just fine, but I guess this isn't the case? For your second question, Handling sessions during SSR is similar to how the documentation and GitHub both describe.
Copy code
js
// ...imports

ensureSuperTokensInit();

async function getSSRSessionHelper(): Promise<{ session: SessionContainer | undefined; hasToken: boolean; hasInvalidClaims: boolean, error: Error | undefined }> {
    let session: SessionContainer | undefined;
    let hasToken = false;
    let hasInvalidClaims = false;
    let error: Error | undefined = undefined;

    try {
        ({ session, hasToken, hasInvalidClaims } = await getSSRSession(cookies().getAll(), headers()));
    } catch (err: any) {
        error = err;
    }
    return { session, hasToken, hasInvalidClaims, error };
}

export default async function DashboardLayout({
    children,
}: {
    children: React.ReactNode;
}) {
    const { session, hasToken, hasInvalidClaims, error } = await getSSRSessionHelper();

    if (error) {
        return (
            <div>
                Something went wrong while trying to get the session. Error - {error.message}
            </div>
        )
    }

    if (!session) {
        if (!hasToken) {
        // Note that this is triggered often.
            console.log("Redirecting to auth")
            return redirect("https://sub.domain.etx/auth");
        }

        if (hasInvalidClaims) {
            console.log("Redirecting to SessionAuthForNextJS")
            return <SessionAuthForNextJS />;
        } else {
            console.log("Redirecting to tryRefreshComponent")
            return <TryRefreshComponent />;
        }
    }

    /*
 
    Some fetch logic...

    */

    return (
        <SessionAuthForNextJS>
            <div className="text-white">
        {/* ...PageInformation */}
        </div>
    </SessionAuthForNextJS>
    );
}
If you are interested in the middleware code I briefly implemented, before realizing it was meant for APIs.
Copy code
js
if (request.nextUrl.pathname.includes("/dashboard") || request.nextUrl.pathname.includes("/account") || request.nextUrl.pathname.includes("/admin")) {
    return withSession(request, async (err, session) => {
      console.log(session);
      if (err) {
        console.log("Had a verification frontend error", err);
        return NextResponse.redirect("https://sub.domain.etx/auth")
      }
      if (session === undefined) {
        console.log("Session is undefined");
        return NextResponse.next();
      }

      console.log("Continuing with headers")
      return NextResponse.next({
        headers: {
          ...requestHeaders,
          "x-user-id": session.getUserId(),
        },
      });
    });
  }
r
so i think you should figure out why the cookies().getAll() is empty. Solving that would solve the issue you face. The code in our docs are right.
a
Yea, I just recently, today, found out that this is the issue. I guess sometimes you just need to say your problem out loud in order to understand the underlying issue behind it, lol.
r
makes sense!
7 Views