DEV Community 👩‍💻👨‍💻

Cover image for Cookies with React, Express and Docker
Mac
Mac

Posted on

Cookies with React, Express and Docker

Recently I had to move JWT to cookies to used it between Docker microservises seamlessly. We run each container on separate url prefix with was tricker to figure out as cookie have to be there when url is changed. I couldn’t find any straightforward solution so I decided to write it as it might be useful for someone or even for future me.

I just assume you have working front-end and back-end containers and everything run fine. I won’t be explaining what cookies are as there’re plenty of better articles on that topic.

Basic setup

Let’s use simple Express server as example how to send cookies.

// index.js

const express = require("express")
const session = require("express-session");

const app = express()

app.use(
  session({
    secret: process.env.LOGIN_SERVER_SECRET,
    saveUninitialized: true,
    resave: true,
    cookie: {
      httpOnly: false,
      secure: false,
    },
  }),
);

app.get("/cookie", (req, res) => {
  const options = {
    secure: false,
    httpOnly: false,
    domain: ".your.domain.com"
  }

  return res
    .cookie("cookieName", "cookieValue", options)
    .status(200)
    .send("cookie sent")
})

app.listen(8080)

In this case whenever we send request to localhost:8080/cookie server responds with Set-Cookie header. That works fine when you type it directly in your browser or in app like Postman. Problem starts when you run you client on client.your.domain.com and server on server.your.domain.com. We start getting CORS issues.

Let’s see basic setup for out client app. I used create-react-app and just modified it by adding superagent (great library for requests) and sending request whenever I click on the link.

// App.js

import React from "react";
import superagent from "superagent";
import logo from "./logo.svg";
import "./App.css";

function App() {

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          onClick={() =>
            superagent
              .get("http://localhost:8080/cookie")
              .then(response => {
                console.log(response);
              })
          }
        >
          Get Cookie
        </a>
      </header>
    </div>
  );
}

export default App;

CORS

Since we sending request from different originator we get CORS issues. Simple solution to do that is to install cors package and add it as by simple example in their docs.

Again, simple cors with wildcard (*/*) won’t work. We have to set up some custom config for cors and catch pre-flight OPTION check.

// index.js

const express = require("express")
const session = require("express-session");
const cors = require("cors")
const app = express()

app.use(
  session({
    secret: process.env.LOGIN_SERVER_SECRET,
    saveUninitialized: true,
    resave: true,
    cookie: {
      httpOnly: false,
      secure: false,
    },
  }),
);

const corsOptions = {
  origin: /\.your.domain\.com$/,    // reqexp will match all prefixes
  methods: "GET,HEAD,POST,PATCH,DELETE,OPTIONS",
  credentials: true,                // required to pass
  allowedHeaders: "Content-Type, Authorization, X-Requested-With",
}

// intercept pre-flight check for all routes
app.options('*', cors(corsOptions))

// add cors middleware to route 
app.get("/cookie", cors(corsOptions), (req, res) => {
  const options = {
    secure: false,
    httpOnly: false,
    domain: ".your.domain.com"
  }

return res
    .cookie("cookieName", "cookieValue", options)
    .status(200)
    .send("cookie sent")
})

app.listen(8080)

There is one more change on the front-end. Since our server now accepts request with credentials we have to send one to pass the cookie. It’s literally one line extra

// App.js

import React from "react";
import superagent from "superagent";
import logo from "./logo.svg";
import "./App.css";

function App() {

return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          onClick={() =>
            superagent
              .get("http://localhost:8080/cookie")
              .withCredentials()           // it's simple as that
              .then(response => {
                console.log(response);
              })
          }
        >
          Get Cookie
        </a>
      </header>
    </div>
  );
}

export default App;

Secured cookies

As you might noticed i used unsecured cookies in above examples. Thet’s for dev/local purposes only. If you want to use it in production you have to take care about security. Secure cookies will work only on https so you have to take care of that as well. Good idea is to set cookies security dependent on NODE_ENV, so we don’t have to remember about it when working on dev and then deploy to prod.

// index.js

===

app.use(
  session({
    secret: process.env.LOGIN_SERVER_SECRET,
    saveUninitialized: true,
    resave: true,
    cookie: {
      httpOnly: true,            // change both to true
      secure: true,
    },
  }),
);

===

// dynamic change
const isCookieSecure = 
  process.env.NODE_ENV === "production" ? true : false;

// add cors middleware to route 
app.get("/cookie", cors(corsOptions), (req, res) => {
  const options = {
    secure: isCookieSecure,
    httpOnly: isCookieSecure,
    domain: ".your.domain.com"
  }


return res
    .cookie("cookieName", "cookieValue", options)
    .status(200)
    .send("cookie sent")
})

That’s basically it. You can add as many apps and servers to your docker as you want and enjoy cookies everywhere. They’ll be passed automatically in the request and responses headers. Thanks you all for getting that far, hopefully that’s gonna be useful for someone :)

Read more about CORS, OPTIONS and cookies at MDN. Any questions or feedback just post a comment. Thanks 🙏

Top comments (1)

Collapse
 
moisesrj97 profile image
Moisés Rodríguez

Hi!l I know this is an old post, but maybe I'll get a response. I've been learning React and Express apart in different courses, but It's being hard for me to put them together. How can you make the express-session cookie to appear on the react frontend client?

🌚 Browsing with dark mode makes you a better developer by a factor of exactly 40.

It's a scientific fact.