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.
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.
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:
-
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.
-
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:
-
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.
-
Secure Attribute: Set the
Secureattribute on cookies to ensure that they are only sent over secure HTTPS connections. This -
HttpOnly Attribute: Set the
HttpOnlyattribute on cookies to prevent them from being accessed by client-side scripts. This helps protect against cross-site scripting (XSS) attacks. -
SameSite Attribute: Set the
SameSiteattribute 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:
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:
{
"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:
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}`);
});
usernameis the name of the cookie.'rizwan'is the value stored in the cookie.{ maxAge: 900000, httpOnly: true }are the options wheremaxAgespecifies how long the cookie should last (in milliseconds), andhttpOnly: truemakes 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:
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:
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:
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}`);
});
3. Verifying a JWT from a Cookie
To verify the JWT stored in the cookie, you can create a protected route that checks the JWT:
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:
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!