Using cookies

The HTTP protocol is stateless. Any given request has no information on previous requests. For a server, this meant that determining if two requests originated from the same browser was not possible. Cookies were invented to solve this problem. Cookies are primarily used to share state between clients (usually a browser) and a server, existing as small text files stored in browsers.

Cookies are insecure. Cookie information flows between a server and a client in plain text. There is any number of tamper points in between. Browsers allow easy access to them, for example. This is a good idea, as nobody wants information on their browser or local machine to be hidden from them, beyond their control.

Nevertheless, cookies are also used rather extensively to maintain state information, or pointers to state information, particularly in the case of user sessions or other authentication scenarios.

It is assumed that you are familiar with how cookies function in general. Here, we will discuss how cookies are fetched, parsed, and set by a Node HTTP server. We will use the example of a server that echoes back the value of a sent cookie. If no cookie exists, the server will create that cookie and instruct the client to ask for it again.

Consider the following code:

const http = require('http');
const url = require('url');
http.createServer((request, response) => {
let cookies = request.headers.cookie;
if(!cookies) {
let cookieName = "session";
let cookieValue = "123456";
let numberOfDays = 4;
let expiryDate = new Date();
expiryDate.setDate(expiryDate.getDate() + numberOfDays);

let cookieText = `${cookieName}=${cookieValue};expires=${expiryDate.toUTCString()};`;
response.setHeader('Set-Cookie', cookieText);
response.writeHead(302, {'Location': '/'});
return response.end();
}

cookies.split(';').forEach(cookie => {
let m = cookie.match(/(.*?)=(.*)$/);
cookies[m[1].trim()] = (m[2] || '').trim();
});

response.end(`Cookie set: ${cookies.toString()}`);
}).listen(8080);

First, we create a server that checks request headers for cookies:

let server = http.createServer((request, response) => {
let cookies = request.headers.cookie;
...

Note that cookies are stored as the cookie attribute of request.headers. If no cookies exist for this domain, we will need to create one, giving it the name session and a value of 123456:

if (!cookies) {
...
let cookieText = `${cookieName}=${cookieValue};expires=${expiryDate.toUTCString()};`;
response.setHeader('Set-Cookie', cookieText);
response.writeHead(302, {
'Location': '/'
});
return response.end();
}

If we have set this cookie for the first time, the client is instructed to make another request to this same server, using a 302 Found redirect, instructing the client to call our server location again. As there is now a cookie set for this domain, the subsequent request will contain our cookie, which we handle next:

cookies.split(';').forEach(cookie => {
let m = cookie.match(/(.*?)=(.*)$/);
cookies[m[1].trim()] = (m[2] || '').trim();
});
response.end(`Cookie set: ${cookies.toString()}`);

Now if you visit localhost:8080 you should see something like this displayed:

Cookie set: AuthSession=c3Bhc3F1YWxpOjU5QzkzRjQ3OosrEJ30gDa0KcTBhRk-YGGXSZnT; io=QuzEHrr5tIZdH3LjAAAC