Skip to main content

Cookies

Introduction

Cookies are small pieces of data stored in the client's browser. They are used to store information about the user's session (JWT, access token, refresh token, etc), preferences,tracking user activity, and other data that the server needs to remember. Cookies are sent to the server with every request made by the client, allowing the server to identify the client and provide a personalized experience.

Use case

HTTP is a stateless protocol, meaning that the server does not maintain any information about the client between requests. This poses a problem for web applications that require session management, such as e-commerce websites, social media platforms, online banking services, etc. Cookies provide a way to store session information on the client's browser and retrieve it when needed.

Working of Cookies

When a client makes a request to a server, the server can send a Set-Cookie header in the response to set a cookie in the client's browser. The Set-Cookie header contains the name and value of the cookie, along with optional attributes such as the expiration date, domain, and path.

Response with Set-Cookie Header
HTTP/1.1 200 OK
Content-Type: text/html
Set-Cookie: sessionId=12345; Expires=Wed, 21 Oct 2022 07:28:00 GMT; Path=/

The client stores the cookie in its browser and sends it back to the server with every subsequent request. The server can read the cookie from the Cookie header in the request and use the information stored in the cookie to customize the response.

Request with Cookie Header
GET /profile HTTP/1.1
Host: example.com
Cookie: sessionId=12345

In this example, the server sets a cookie named sessionId with the value 12345 in the client's browser. The client sends the cookie back to the server with a request to the /profile endpoint, allowing the server to identify the client and provide personalized content.

Types of Cookies

There are two main types of cookies:

  1. Session Cookies: Session cookies are temporary cookies that are stored in the client's browser for the duration of the session. They are deleted when the browser is closed and are commonly used for session management.

  2. Persistent Cookies: Persistent cookies are stored in the client's browser for a specified period of time, even after the browser is closed. They are used to remember user preferences, login information, and other long-term data.

Security Considerations

While cookies are a convenient way to store information on the client's browser, they also pose security risks if not handled properly. Here are some security considerations when working with cookies:

  1. Sensitive Data: Avoid storing sensitive information such as passwords, credit card numbers, and personal details in cookies. Use secure methods such as HTTPS and encryption to protect sensitive data.

  2. Secure Attribute: Set the Secure attribute on cookies to ensure that they are only sent over secure HTTPS connections. This

  3. HttpOnly Attribute: Set the HttpOnly attribute on cookies to prevent them from being accessed by client-side scripts. This helps protect against cross-site scripting (XSS) attacks.

  4. SameSite Attribute: Set the SameSite attribute on cookies to prevent cross-site request forgery (CSRF) attacks. This attribute restricts the cookie to be sent only in same-site requests.

By following best practices and implementing proper security measures, you can ensure that cookies are used safely and securely in your web applications.

Let's write some code

Now that you have a basic understanding of cookies, let's write some code to work with cookies in an Express application. We'll create a simple web server that sets a cookie in the client's browser and reads it from subsequent requests.

In an Express.js application, handling cookies is straightforward, thanks to middleware like cookie-parser. This post will walk you through the basics of using cookies in Express.

1. Setting Up Your Express App

Before we dive into using cookies, make sure you have a basic Express application set up. If not, you can create one using the following commands:

Terminal
mkdir express-cookies-app
cd express-cookies-app
npm init -y
npm install express cookie-parser

Update the package.json file, and add type: "module" to enable ES modules, and set up the start script:

package.json
{
"name": "express-cookies-app",
"version": "1.0.0",
"type": "module",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.17.1",
"cookie-parser": "^1.4.5"
}
}

Now, create a basic Express server in index.js:

index.js
import express from "express";
import cookieParser from "cookie-parser";

const app = express();
const port = 3006;

// Use cookie-parser middleware
app.use(cookieParser());

app.get("/", (req, res) => {
res.send("Welcome to the Express Cookies Tutorial!");
});

app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});

2. Setting Cookies

To set a cookie in Express, you can use the res.cookie() method. This method takes the cookie name, value, and optional options as arguments.

Here’s an example route that sets a cookie:

import express from "express";
import cookieParser from "cookie-parser";

const app = express();
const port = 3006;

app.use(cookieParser());

app.get("/set-cookie", (req, res) => {
res.cookie("username", "rizwan", { maxAge: 900000, httpOnly: true });
res.send("Cookie has been set!");
});

app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
  • username is the name of the cookie.
  • 'rizwan' is the value stored in the cookie.
  • { maxAge: 900000, httpOnly: true } are the options where maxAge specifies how long the cookie should last (in milliseconds), and httpOnly: true makes the cookie inaccessible to JavaScript on the client-side, enhancing security.

3. Reading Cookies

To read cookies in your Express app, use the req.cookies object provided by the cookie-parser middleware:

index.js
import express from "express";
import cookieParser from "cookie-parser";

const app = express();
const port = 3006;

app.use(cookieParser());

app.get("/set-cookie", (req, res) => {
res.cookie("username", "rizwan", { maxAge: 900000, httpOnly: true });
res.send("Cookie has been set!");
});

app.get("/get-cookie", (req, res) => {
const username = req.cookies.username;
if (username) {
res.send(`Hello, ${username}!`);
} else {
res.send("No cookie found.");
}
});

app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});

This code checks if the username cookie exists and returns a greeting. If the cookie doesn’t exist, it returns a message saying no cookie was found.

4. Deleting Cookies

Deleting a cookie is just as simple as setting one. Use the res.clearCookie() method:

index.js
import express from "express";
import cookieParser from "cookie-parser";

const app = express();
const port = 3006;

app.use(cookieParser());

app.get("/set-cookie", (req, res) => {
res.cookie("userId", "12345", { maxAge: 900000, httpOnly: true });
res.cookie("username", "rizwan", { maxAge: 900000, httpOnly: true });
res.cookie("email", "example@rizwan.com", { maxAge: 900000, httpOnly: true });
res.send("Cookie has been set!");
});

app.get("/get-cookie", (req, res) => {
const username = req.cookies.username;
if (username) {
res.send(`Hello, ${username}!`);
} else {
res.send("No cookie found.");
}
});

app.get("/delete-cookie", (req, res) => {
res.clearCookie("username");
res.send("Cookie has been deleted!");
});

app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});

This will remove the username cookie from the client’s browser.

5. Secure and Signed Cookies

For security purposes, you may want to sign cookies to prevent tampering. You can do this by passing a secret to cookie-parser:

app.use(cookieParser("yourSecretKey"));

To set a signed cookie:

app.get("/set-signed-cookie", (req, res) => {
res.cookie("signedUsername", "JaneDoe", {
signed: true,
maxAge: 900000,
httpOnly: true,
});
res.send("Signed cookie has been set!");
});

To read a signed cookie:

app.get("/get-signed-cookie", (req, res) => {
const signedUsername = req.signedCookies.signedUsername;
if (signedUsername) {
res.send(`Hello, ${signedUsername}! (signed)`);
} else {
res.send("No signed cookie found.");
}
});

Handling JWT in Cookies

JWT is a commonly used token for securing APIs and handling user authentication. You can store JWTs in cookies to keep the user authenticated across sessions.

Let's use the same Express app which we created earlier and modify it to handle JWT in cookies.

1. Install Dependencies

First, install the jsonwebtoken package:

npm install jsonwebtoken

2. Create Login API

Create a simple login API that generates a JWT and stores it in a cookie:

index.js
import express from "express";
import cookieParser from "cookie-parser";
import jwt from "jsonwebtoken";

const app = express();
const port = 3006;
const JWT_SECRET = "yourSecretKey";

app.use(cookieParser("yourSecretKey")); // For signed cookies

app.post("/login", (req, res) => {
// this is just a dummy API, you can replace it with your own authentication logic
// Authenticate user
const user = {
username: req.body.username ?? "rizwan",
email: req.body.email ?? "example@rizwan.com",
};

// Generate JWT
const token = jwt.sign(user, JWT_SECRET, { expiresIn: "1h" });

// Set JWT as a cookie
res.cookie("authToken", token, {
httpOnly: true, // Accessible only by the web server
secure: true, // Ensure the browser only sends the cookie over HTTPS
maxAge: 3600000, // 1 hour
});

res.send("Logged in and JWT set in cookie!");
});

app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});

To verify the JWT stored in the cookie, you can create a protected route that checks the JWT:

index.js
import express from "express";
import cookieParser from "cookie-parser";
import jwt from "jsonwebtoken";

const app = express();
const port = 3006;
const JWT_SECRET = "yourSecretKey";

app.use(cookieParser("yourSecretKey")); // For signed cookies

app.post("/login", (req, res) => {
// this is just a dummy API, you can replace it with your own authentication logic
// Authenticate user
const user = {
username: req.body.username ?? "rizwan",
email: req.body.email ?? "example@rizwan.com",
};

// Generate JWT
const token = jwt.sign(user, JWT_SECRET, { expiresIn: "1h" });

// Set JWT as a cookie
res.cookie("authToken", token, {
httpOnly: true, // Accessible only by the web server
secure: true, // Ensure the browser only sends the cookie over HTTPS
maxAge: 3600000, // 1 hour
});

res.send("Logged in and JWT set in cookie!");
});

app.get("/dashboard", (req, res) => {
// Get JWT from cookie
const token = req.cookies.authToken;

if (!token) {
return res.status(401).send("Access Denied");
}

try {
// Verify JWT
const verifiedUser = jwt.verify(token, JWT_SECRET);
res.send(`Welcome to your dashboard, ${verifiedUser.username}`);
} catch (error) {
res.status(400).send("Invalid Token");
}
});

app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});

In this example, the /dashboard route checks if the authToken cookie exists and verifies the JWT. If the JWT is valid, it returns a welcome message; otherwise, it returns an error.

4. Logging Out and Clearing Cookies

To log out a user and clear the JWT cookie, you can create a logout route:

index.js
import express from "express";
import cookieParser from "cookie-parser";
import jwt from "jsonwebtoken";

const app = express();
const port = 3006;
const JWT_SECRET = "yourSecretKey";

app.use(cookieParser("yourSecretKey")); // For signed cookies

app.post("/login", (req, res) => {
// this is just a dummy API, you can replace it with your own authentication logic
// Authenticate user
const user = {
username: req.body.username ?? "rizwan",
email: req.body.email ?? "example@rizwan.com",
};

// Generate JWT
const token = jwt.sign(user, JWT_SECRET, { expiresIn: "1h" });

// Set JWT as a cookie
res.cookie("authToken", token, {
httpOnly: true, // Accessible only by the web server
secure: true, // Ensure the browser only sends the cookie over HTTPS
maxAge: 3600000, // 1 hour
});

res.send("Logged in and JWT set in cookie!");
});

app.get("/dashboard", (req, res) => {
// Get JWT from cookie
const token = req.cookies.authToken;

if (!token) {
return res.status(401).send("Access Denied");
}

try {
// Verify JWT
const verifiedUser = jwt.verify(token, JWT_SECRET);
res.send(`Welcome to your dashboard, ${verifiedUser.username}`);
} catch (error) {
res.status(400).send("Invalid Token");
}
});

app.get("/logout", (req, res) => {
res.clearCookie("authToken");
res.send("Logged out successfully!");
});

app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});

Code

You can download the related code from here.

Conclusion

Using cookies in Express.js is a powerful way to manage state and session data in your web applications. With the cookie-parser middleware, setting, reading, and deleting cookies is simple and secure. Whether you’re building a simple website or a complex web application, understanding how to work with cookies will enhance your ability to manage user sessions and preferences effectively.

Happy coding!