Cross-(sub)domains cookies pitfalls

This post is to describe the problem I had to configure a project to share cookies with all subdomains. It was supposed to be a simple task, but I spent many hours to figure out what I was doing wrong.

My setup

I have a SPA running on app.coldletter.com that calls /login at api.coldletter.com using axios.

First change

authRouter.post("/login", async (req, res) => {
  const loginRequest = req.body;
  const ret = await authService.login(loginRequest);

  res.cookie("sessionToken", ret.sessionId, {
    signed: true,
    secure: config.isProduction,
    maxAge: 1000 * 60 * 60 * 24 * 35,
    domain: "coldletter.com",
  });

  res.status(200).send(ret);
});

Great! After logging in, I noticed that the cookie is available in the browser of any subdomain. But… the cookie was never sent to the server.

After trying many different things, I realized that axios does not send cross-domain cookies by default. So I had to make the following change in the request in my SPA:

return axios(`${API_URL()}/login`, {
  withCredentials: true, /// <<<< Here is the change !!!
  method: "POST",
  data: {
    email,
    password,
  },
});

Now, a new error happened…

The new error

Access to XMLHttpRequest at '...' from origin '...' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

Yeah, I was using app.use(cors()) in this app.

Configuring CORS properly solved the problem.

app.use(
  cors({
    credentials: true,
    origin: [
      "https://coldletter.com",
      "https://api.coldletter.com",
      "https://app.coldletter.com",
    ],
  }),
);