Search code examples
typescriptsolid-js

Storing Cookies in SolidJs is giving me problems


I have a login route that looks as follows:

import {
  Title,
  createCookieSessionStorage,
  parseCookie,
  useNavigate,
  useServerContext,
} from "solid-start";
import styles from "./login.module.css";
import { A, useRouteData } from "@solidjs/router";
import {
  createServerAction$,
  createServerData$,
  redirect,
} from "solid-start/server";
import { TOKEN_KEY } from "~/constants";
import { ErrorType, MeType } from "~/types";
import { Component, createEffect } from "solid-js";
export const routeData = () => {
  return createServerData$(async (_, event) => {
    const cookies = parseCookie(event.request.headers.get("Cookie") ?? "");
    const token = cookies[TOKEN_KEY] ?? "";
    const res = await event.fetch("http://127.0.0.1:3001/auth/me", {
      headers: {
        Authorization: `Bearer ${token}`,
      },
      credentials: "include",
    });
    const { me }: { me: MeType | null } = await res.json();
    return {
      me,
    };
  });
};

const Login: Component<{}> = () => {
  const { latest } = useRouteData<typeof routeData>();
  const nav = useNavigate();
  createEffect(async () => {
    if (!!latest?.me) {
      nav("/", { replace: true });
    }
  });

  const storage = createCookieSessionStorage({
    cookie: {
      name: "session",
      secure: false,
      secrets: ["je"],
      sameSite: "lax",
      path: "/",
      maxAge: 60 * 60 * 24 * 30, // 30 days
      httpOnly: true,
    },
  });

  const [submitting, { Form }] = createServerAction$(
    async (form: FormData, { request, fetch }) => {
      const _data: { email: string; password: string } = {
        password: form.get("password") as string,
        email: form.get("email") as string,
      };

      const res = await fetch("http://127.0.0.1:3001/auth/login", {
        credentials: "include",
        body: JSON.stringify({
          ..._data,
        }),
        method: "POST",
        headers: {
          Accept: "application/json",
          "Content-Type": "application/json",
        },
      });
      const data: {
        user: MeType | null;
        error: ErrorType | null;
        jwt: string | null;
      } = await res.json();

      if (data.user && data.jwt) {
        throw redirect("/");
      }
      return { data };
    }
  );
  return (
    <main>
      <Title>Login</Title>
      <div class={styles.login}>
        <Form>
          <img alt="logo" src="/icon.png" />
          <h1>Login</h1>
          <input name="email" type="email" placeholder="email" />
          <input name="password" type="password" placeholder="password" />

          <div class={styles.error}>
            {submitting.result?.data.error?.field &&
              submitting.result.data.error.message}
          </div>
          <button type="submit">Login</button>
          <p>
            Already have an account? <span></span>
          </p>
          <A href="/auth/login">Login</A>
        </Form>
      </div>
    </main>
  );
};
export default Login;

I have an authentication server that returns me a jwt token on succesfull authentication. But how can i store that token in the cookie. Ivé tried to read the docs and follows example but seems like they are not working https://start.solidjs.com/advanced/session.


Solution

  • How to set cookies with SolidJS on the Server

    Using solid-start, on the server, you can use a SessionStorage.

    import { createCookieSessionStorage } from 'solid-start';
     
    const storage = createCookieSessionStorage({
      cookie: {
        name: "session",
        secure: process.env.NODE_ENV === "production",
        secrets: [process.env.SESSION_SECRET],
        sameSite: "lax",
        path: "/",
        maxAge: 60 * 60 * 24 * 30, // 30 days
        httpOnly: true
      }
    });
    

    Then, you can create a cookie like so:

    async function login(request: Request, userId: string) {
      const session = await storage.getSession(request.headers.get("Cookie"));
      session.set("userId", userId);
      const response = new Response("Logged in", {
        headers: {
          "Set-Cookie": await storage.commitSession(session)
        }
      });
    }
    

    How to set cookies with SolidJS on the Client

    You can simply set cookies using any JavaScript package, or do it directly which might look something like this:

    document.cookie = `${YOUR_COOKIE_NAME}=; expires=Thu, 01 Jan 2024 00:00:00 UTC; domain=${YOUR_DOMAIN}; path=/; secure;`;
    

    How to set cookies on the server while invoking the function on the server or the client

    You can also use Solid's remote-procedure style calls to simply call a function and have it always run on the server. This adds some latency just to set a cookie by it may be the right choice depending on what you are doing. To do that, you can use createServerAction$. That might look something like this:

    createServerAction$(async (form: FormData, { request }) => {
      const session = await storage.getSession(request.headers.get("Cookie"));
      session.set("userId", userId);
      const response = new Response("Logged in", {
        headers: {
          "Set-Cookie": await storage.commitSession(session)
        }
      });
    }