@worker-tools/middleware
Version:
A suite of standalone HTTP server middlewares for Worker Runtimes.
136 lines • 5.38 kB
JavaScript
import { RequestCookieStore } from "@worker-tools/request-cookie-store";
import { SignedCookieStore } from "@worker-tools/signed-cookie-store";
import { EncryptedCookieStore } from "@worker-tools/encrypted-cookie-store";
import { ResolvablePromise } from '@worker-tools/resolvable-promise';
import { forbidden } from "@worker-tools/response-creators";
import { MiddlewareCookieStore } from "./utils/middleware-cookie-store.js";
import { headersSetCookieFix } from './utils/headers-set-cookie-fix.js';
import { unsettle } from "./utils/unsettle.js";
export async function cookiesFrom(cookieStore) {
return Object.fromEntries((await cookieStore.getAll()).map(({ name, value }) => [name, value]));
}
export const plainCookies = () => async (ax) => {
const x = await ax;
const cookieStore = new RequestCookieStore(x.request);
const requestDuration = new ResolvablePromise();
const unsignedCookieStore = new MiddlewareCookieStore(cookieStore, requestDuration);
const unsignedCookies = await cookiesFrom(unsignedCookieStore);
const nx = Object.assign(x, {
cookieStore: unsignedCookieStore,
cookies: unsignedCookies,
unsignedCookieStore,
unsignedCookies,
});
x.effects.push(response => {
requestDuration.resolve();
const { headers: cookieHeaders } = cookieStore;
if (cookieHeaders.length)
response.headers.append('VARY', 'Cookie');
const { status, statusText, headers, body } = response;
return new Response(body, {
status,
statusText,
headers: [
...headersSetCookieFix(headers),
...cookieHeaders,
],
});
});
return nx;
};
export { plainCookies as unsignedCookies };
export const signedCookies = (opts) => {
// TODO: options to provide own cryptokey??
// TODO: What if secret isn't known at initialization (e.g. Cloudflare Workers)
if (!opts.secret)
throw TypeError('Secret missing');
const keyPromise = SignedCookieStore.deriveCryptoKey(opts);
return async (ax) => {
const x = await ax;
const request = x.request;
const cookieStore = new RequestCookieStore(request);
const requestDuration = new ResolvablePromise();
const signedCookieStore = new MiddlewareCookieStore(new SignedCookieStore(cookieStore, await keyPromise, {
keyring: opts.keyring
}), requestDuration);
let signedCookies;
try {
signedCookies = await cookiesFrom(signedCookieStore);
}
catch {
throw forbidden();
}
const nx = Object.assign(x, {
cookieStore: signedCookieStore,
cookies: signedCookies,
signedCookieStore,
signedCookies,
});
x.effects.push(async (response) => {
// Wait for all set cookie promises to settle
requestDuration.resolve();
await unsettle(signedCookieStore.allSettledPromise);
const { headers: cookieHeaders } = cookieStore;
if (cookieHeaders.length)
response.headers.append('VARY', 'Cookie');
const { status, statusText, headers, body } = response;
return new Response(body, {
status,
statusText,
headers: [
...headersSetCookieFix(headers),
...cookieHeaders,
],
});
});
return nx;
};
};
export const encryptedCookies = (opts) => {
// TODO: options to provide own cryptokey??
// TODO: What if secret isn't known at initialization (e.g. Cloudflare Workers)
if (!opts.secret)
throw TypeError('Secret missing');
const keyPromise = EncryptedCookieStore.deriveCryptoKey(opts);
return async (ax) => {
const x = await ax;
const request = x.request;
const cookieStore = new RequestCookieStore(request);
const requestDuration = new ResolvablePromise();
const encryptedCookieStore = new MiddlewareCookieStore(new EncryptedCookieStore(cookieStore, await keyPromise, {
keyring: opts.keyring
}), requestDuration);
let encryptedCookies;
try {
encryptedCookies = await cookiesFrom(encryptedCookieStore);
}
catch {
throw forbidden();
}
const nx = Object.assign(x, {
cookieStore: encryptedCookieStore,
cookies: encryptedCookies,
encryptedCookieStore,
encryptedCookies,
});
x.effects.push(async (response) => {
// Wait for all set cookie promises to settle
requestDuration.resolve();
await unsettle(encryptedCookieStore.allSettledPromise);
const { headers: cookieHeaders } = cookieStore;
if (cookieHeaders.length)
response.headers.append('VARY', 'Cookie');
const { status, statusText, headers, body } = response;
return new Response(body, {
status,
statusText,
headers: [
...headersSetCookieFix(headers),
...cookieHeaders,
],
});
});
return nx;
};
};
//# sourceMappingURL=cookies.js.map