@taxum/cookie
Version:
Cookie jar for Taxum
81 lines • 2.7 kB
JavaScript
import { createHmac, timingSafeEqual } from "node:crypto";
const BASE64_DIGEST_LEN = 44;
/**
* A child cookie jar that authenticates its cookies.
*
* A _signed_ child jar signs all the cookies added to it and verifies cookies
* retrieved from it. Any cookies stored in the `SignedJar` are provided
* integrity and authenticity. In other words, clients cannot tamper with the
* contents of a cookie, nor can they fabricate cookie values, but the data is
* visible in plaintext.
*/
export class SignedJar {
parent;
key;
/**
* @internal
*/
constructor(parent, key) {
this.parent = parent;
this.key = key;
}
/**
* Returns a {@link Cookie} with the name `name` from the parent jar.
*
* It verifies the authenticity and integrity of the cookie's value,
* returning a `Cookie` with the authenticated value. If the cookie cannot
* be found, or the cookie fails to verify, `null` is returned.
*
* @see {@link CookieJar.get}
*/
get(name) {
const cookie = this.parent.get(name);
return cookie ? this.verify(cookie) : null;
}
/**
* Adds a {@link Cookie} to the parent jar.
*
* The cookie's value is signed assuring integrity and authenticity.
*
* @see {@link CookieJar.add}
*/
add(cookie) {
this.parent.add(this.sign(cookie));
}
/**
* Removes a {@link Cookie} from the parent jar.
*
* For correct removal, the passed in `cookie` must contain the same `path`
* and `domain` as the cookie that was initially set.
*
* @see {@link CookieJar.remove}
*/
remove(cookie) {
this.parent.remove(cookie);
}
sign(cookie) {
const hmac = createHmac("sha256", this.key);
hmac.update(cookie.name);
hmac.update(cookie.value);
const signature = hmac.digest("base64");
return cookie.withValue(`${signature}${cookie.value}`);
}
verify(cookie) {
const value = this.verifyValue(cookie.name, cookie.value);
return value !== null ? cookie.withValue(value) : null;
}
verifyValue(name, cookieValue) {
const signature = cookieValue.slice(0, BASE64_DIGEST_LEN);
const value = cookieValue.slice(BASE64_DIGEST_LEN);
const digest = Buffer.from(signature, "base64");
const hmac = createHmac("sha256", this.key);
hmac.update(name);
hmac.update(value);
const actualDigest = hmac.digest();
if (digest.length !== actualDigest.length) {
return null;
}
return timingSafeEqual(digest, actualDigest) ? value : null;
}
}
//# sourceMappingURL=signed.js.map