Skip to content

flipeador/node-http-cookies

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

HTTP Cookies

Simplifies the handling of HTTP cookies.

Features

  • Create string or JSON cookies with an optional signature.
  • Parse the Cookie header into a key-value Object.
  • Use the cookie parser as a middleware in Express.

Note

This is a lightweight alternative library to cookie-parser with no dependencies.

Installation

npm i flipeador/node-http-cookies#semver:^2.0.0

Cookie Attributes

Cookies are mainly used for storing credentials, user preferences, site settings, and tracking data.

  • The cookie name can contain any US-ASCII characters except for control characters, space, tab, and separators (( ) < > @ , ; : \ " / [ ] ? = { }).
  • The cookie value can optionally be wrapped in double quotes and include any US-ASCII character excluding control characters, whitespace, double quotes, commas, semicolons, and backslashes.
  • The cookie name or value can be URI encoded to satisfy the allowed character requirement.
  • The cookie value can be a number, or a JSON-stringified object prefixed with j:.
  • The cookie value can be signed to ensure that it has not been tampered with, and prefixed with s:.

The server sends cookies to the browser by setting the Set-Cookie header:

[
    '<cookie-name>=<cookie-value>; attribute1; attribute2; ...',
    ...
]

The browser sends cookies back to the server by setting the Cookie header:

'<cookie-name>=<cookie-value>; ...'

Cookie names can have specific semantics via the following prefixes:

Prefix Description
__Secure- Require the cookie to be set from a secure page (HTTPS).
__Host- Make the cookie bound to the hostname (and not the registrable domain).

The following are the cookie attributes recognized by the library:

Attribute Default Description Note
Domain Defines the host to which the cookie will be sent.
Path / Restrict the cookie to a specific path, including subpaths.
Secure false Set the cookie over HTTPS only, except on localhost.
HttpOnly false Make the cookie inaccessible to the JavaScript Document.cookie API.
SameSite lax Origin relationship between request and resource. Values: strict lax none.
Partitioned false The cookie should be stored using partitioned storage. Requires the Secure attribute.
Expires The exact expiration date in the RFC 7231 format. new Date().toUTCString()
Max-Age Number of seconds until the cookie expires. Invalidates Expires.

Note

  • The value true will cause the attribute to be added to the cookie with no value assigned.
  • The values false NaN '' will cause the attribute not to be added to the cookie.
  • The values null undefined will cause the attribute to keep the default value, if any.

The following are special attributes used as options:

Option Description
secret Secret to use to sign the cookie value.

Cookie Lifetime

By specifying the Expires or Max-Age attribute, you can create a persistent cookie. If the server does not provide information about the expiration of the cookie by specifying the exact date or after a specific length of time, it becomes a session cookie and is removed when the browser shuts down.

A cookie can be removed by specifying a zero or negative number in the Max-Age attribute, wich will expire the cookie immediately.

Partitioned Attribute

Cookies Having Independent Partitioned State (CHIPS) allows developers to opt a cookie into partitioned storage, with separate cookie jars per top-level site, which restricts the contexts in which a cookie is available to only those whose top-level document is same-site with the top-level document that initiated the request that created the cookie. Partitioned cookies allows to embedded sites which are cross-site with the top-level frame to have access to HTTP state which cannot be used for tracking across multiple top-level sites.

Google Chrome (≥118):

  • Visit chrome://flags/#test-third-party-cookie-phaseout and enable partitioned cookies.
  • The Application tab shows both partition and unparitioned stored cookies, even if they are not accessible in the current page.
  • Unpartitioned third-party cookies will not be available to embedded sites on different top-level sites.
  • You can better visualize unpartitioned cookies that are being blocked by checking Only show cookies with an issue in the DevTools.

Further reading:

SameSite Attribute

The SameSite attribute allows to declare if the cookie should be restricted to a same-site or cross-site context, providing some protection against Cross-site Request Forgery (CSRF) attacks. If the server stores a session token with full access to the user's account in the cookies, you don't want the browser to send it in a malicious request from an unauthorized site. The included cookie is first-party when the request is made in a same-site context, and third-party when the request is made in a cross-site context.

SameSite Description
Strict Cookies will only be sent in a same-site context.
Lax Like Strict, except the browser also sends the cookie when the user navigates to the cookie's origin site (e.g. by following a link from an external/different site).
None Cookies will be sent in both cross-site and same-site contexts. Requires the Secure attribute.

Important

If the SameSite attribute is set to None, browsers that have enabled the third-party cookie phaseout will require the Partitioned attribute to be present, otherwise unparitioned cookies will be blocked in third-party contexts.

Top-level domains (TLD) are listed in the Root Zone Database. The Public Suffix List (PSL) is a catalog of certain Internet domain names, whose entries in the list are also referred to as effective Top-Level Domains (eTLD). The PSL is used for some domains where the TLD is not granular enough to identify the site.

User agents group URIs together into protection domains called origins. Two URIs are part of the same origin if they have the same scheme, host, and port (RFC 6454). All cross-site requests are necessarily cross-origin, but not all cross-origin requests are cross-site.

The same-site context treats all subdomains of the eTLD+1 to be equivalent to the root domain.

Origin eTLD+1 Same-Site Cross-Site
http://www.example.com .example.com http://example.com http://x.example.com http://x.y.example.com https://www.example.com example.net username.github.io
username.github.io username.github.io x.username.github.io x.y.username.github.io otheruser.github.io

Important

Note

The Sec-Fetch-Site header can be used to determine if the request is same-origin, same-site, cross-site, or it is a user-originated operation (e.g. entering a URL into the address bar, opening a bookmark, or a drag-and-drop operation).

Further reading:

Example

Express

import express from 'express';
import {
    cookieParser,
    parseJsonCookie,
    parseSignedCookie
} from '@flipeador/node-http-cookies';

const app = express();

// Add the middleware here and not individually to the routes.
// This does not have a significant impact on performance,
// as the cookies are not parsed immediately for every request.
// Cookies are parsed only once on first access to the 'req.cookies' property.
// The middleware also sets a function 'res.cookie()' that allows cookies to be added.
app.use(cookieParser());

app.get('/set-cookie', (req, res) => {
    res.cookie('string', 'value', {
        secure: true,
        sameSite: 'none',
        partitioned: true,
        // Restrict the cookie to the current path and subpaths.
        path: `${req.baseUrl}${req.path}`
    });
    res.cookie('json', ['value']);
    res.cookie('signed', 'value', { secret: 'XXX' });
    res.json(req.cookies);
});

app.get('/get-cookie', (req, res) => {
    res.json({
        string: req.cookies.string ??
        'not accessible due to different path',
        json: parseJsonCookie(req.cookies.json),
        signed: parseSignedCookie(req.cookies.signed, 'XXX')
    });
});

app.listen(3000, () => {
    console.log('Server is running!');
    console.log('http://localhost:3000/set-cookie');
    console.log('http://localhost:3000/get-cookie');
});

License

This project is licensed under the Apache License 2.0. See the license file for details.