UNPKG

@worker-tools/middleware

Version:

A suite of standalone HTTP server middlewares for Worker Runtimes.

136 lines 5.38 kB
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