question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Can't set up properly Next.js's SSR

See original GitHub issue

I’ve followed the examples displayed in the README, as well as looked for solutions in other frameworks but I simply can’t fix this problem.

Doing a little bit of debug and research, I can come to the conclusion that the cookie contained by the request may be not what the authStore expects, because, according to the examples, the key pb_auth= is set as the default, while my cookie has the following structure:

a_session_[code]_legacy=[token]

Also, if it has anything to do with this, the token stored in localStorage it’s not equal to the one served on the cookie.

As extra information, I leave the code I wrote.

type ServerRequest = IncomingMessage & {
  cookies: Partial<{
    [key: string]: string
  }>
}

class CustomAuthStore extends BaseAuthStore {
  public request: ServerRequest
  public response: ServerResponse<IncomingMessage>

  constructor (request: ServerRequest, response: ServerResponse<IncomingMessage>) {
    super()

    this.request = request
    this.response = response

    this.loadFromCookie(
      this.request.headers.cookie ?? ''
    )
  }

  save (token: string, model: User | Admin | null): void {
    super.save(token, model)

    this.response.setHeader('set-cookie', this.exportToCookie())
  }

  clear (): void {
    super.clear()

    this.response.setHeader('set-cookie', this.exportToCookie())
  }
}

Versions: pocketbase v0.7.10; pocketbase (sdk) v0.7.4

Issue Analytics

  • State:open
  • Created 10 months ago
  • Comments:17 (4 by maintainers)

github_iconTop GitHub Comments

3reactions
rafifoscommented, Nov 24, 2022

I’ve managed to get it working in the new app directory, using (https://github.com/vvo/iron-session/issues/560#issuecomment-1324598048 as a base. The only thing to note is that any actions that would update the authStore can only happen in the client, fow now.

lib/getUserFromCookie.ts
import type { User } from "@/interfaces";

import { ReadonlyRequestCookies } from "next/dist/server/app-render";

import { pocketbase } from "@/lib";

/**
 * Can be called in page/layout server component.
 * @param cookies ReadonlyRequestCookies
 * @returns User or null
 * @author Arif "poltang" Muslax
 * @see {@link https://github.com/vvo/iron-session/issues/560#issuecomment-1324598048}
 */
function getUserFromCookie(cookies: ReadonlyRequestCookies): User | null {
  const authCookie = cookies.get("pb_auth");

  if (!authCookie) return null;

  pocketbase.authStore.loadFromCookie(`${authCookie.name}=${authCookie.value}`);
  const user = pocketbase.authStore.model;

  return user as unknown as User;
}

export { getUserFromCookie };

lib/pocketbase.ts
import PocketBase from "pocketbase";

import { env } from "@/lib/env";

const pocketbase = new PocketBase(env.POCKETBASE_URL);

/** @see {@link https://github.com/pocketbase/js-sdk/issues/69} */
if (typeof document !== "undefined") {
  pocketbase.authStore.loadFromCookie(document.cookie);

  pocketbase.authStore.onChange(() => {
    document.cookie = pocketbase.authStore.exportToCookie({ httpOnly: false });
  });
}

export { pocketbase };

And then, in

app/layout.tsx
async function RootLayout({ children }: { children: ReactNode }) {
  const user = await getUserFromCookie(cookies());
  console.log(user); // user is available

  if (!user) {
    redirect("/auth/sign-in");
  }

  return (
    <html lang="pt-BR" className={inter.className}>
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="initial-scale=1, width=device-width" />
      </head>

      <body>{children}</body>
    </html>
  );
}
2reactions
samduckercommented, Nov 14, 2022

So what I did for my solution is the following…

  1. Run get serverSideProps whenever I want to detect the logged in state, protect a route, or get some user data
  2. If no cookie is present redirect to /login page
  3. If cookie is present run client.collection(“users”).authRefresh to get the user object, then just return the attributes I want to the client props
import Cookies from "cookies";
import { GetServerSideProps } from "next";
import client from "@services/config";

interface Data {
  firstName: string;
  lastName: string;
  id: string;
  avatar: string;
  isLoggedIn: boolean;
}

export const getServerSideProps: GetServerSideProps<{
  userData: Data;
}> = async (context) => {
  const { req, res } = context;
  const cookies = new Cookies(req, res);
  const pbCookie = cookies.get("pb_auth");

  if (!pbCookie) {
    return {
      redirect: {
        destination: "/login",
        permanent: false,
      },
    };
  }
  client.authStore.loadFromCookie(`pb_auth=${pbCookie}`);
  const data = await getUserProfile();

  const userData = {
    firstName: data.record.firstName,
    lastName: data.record.lastName,
    id: data.record.id,
    avatar: data.record.avatar,
    isLoggedIn: true,
  };

  return {
    props: {
      userData,
    },
  };
};

So the above helps me on the server and then for the client I have the following.

import PocketBase from "pocketbase";

export const client = new PocketBase(
  process.env.NEXT_PUBLIC_POCKETBASE_BASE_URL
);

typeof document !== "undefined" &&
  client.authStore.loadFromCookie(document.cookie);

client.authStore.onChange(() => {
  if (typeof document !== "undefined") {
    document.cookie = client.authStore.exportToCookie({ httpOnly: false });
  }
});

export default client;
Read more comments on GitHub >

github_iconTop Results From Across the Web

react-hydration-error - Next.js
When css-in-js libraries are not set up for pre-rendering (SSR/SSG) it will often lead to a hydration mismatch. In general this means the...
Read more >
Next.js SSR with API not building properly - Stack Overflow
I've set up a very basic blog project with dynamic SSR which fetches data from the Notion-API to generate static blog pages. Everything...
Read more >
Implementing SSR in Next.js: Dynamic routing and prefetching
This happens because named, or static, routes have a higher priority than a dynamic route. For example, if you have declared a dynamic...
Read more >
How to disable Server-Side Rendering in Next.js
Alternatively, you can use dynamic import. We will need to create a wrapper component and wrap any page where you want SSR disabled...
Read more >
How to host a Next.js web app with server-side rendering ...
You can leave all build options as it is, because Amplify will automatically pick up as SSR and deploy it. For this to...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found