| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- // src/utils/cookie.ts
- import { decodeURIComponent_, tryDecode } from "./url.js";
- var algorithm = { name: "HMAC", hash: "SHA-256" };
- var getCryptoKey = async (secret) => {
- const secretBuf = typeof secret === "string" ? new TextEncoder().encode(secret) : secret;
- return await crypto.subtle.importKey("raw", secretBuf, algorithm, false, ["sign", "verify"]);
- };
- var makeSignature = async (value, secret) => {
- const key = await getCryptoKey(secret);
- const signature = await crypto.subtle.sign(algorithm.name, key, new TextEncoder().encode(value));
- return btoa(String.fromCharCode(...new Uint8Array(signature)));
- };
- var verifySignature = async (base64Signature, value, secret) => {
- try {
- const signatureBinStr = atob(base64Signature);
- const signature = new Uint8Array(signatureBinStr.length);
- for (let i = 0, len = signatureBinStr.length; i < len; i++) {
- signature[i] = signatureBinStr.charCodeAt(i);
- }
- return await crypto.subtle.verify(algorithm, secret, signature, new TextEncoder().encode(value));
- } catch {
- return false;
- }
- };
- var validCookieNameRegEx = /^[\w!#$%&'*.^`|~+-]+$/;
- var validCookieValueRegEx = /^[ !#-:<-[\]-~]*$/;
- var trimCookieWhitespace = (value) => {
- let start = 0;
- let end = value.length;
- while (start < end) {
- const charCode = value.charCodeAt(start);
- if (charCode !== 32 && charCode !== 9) {
- break;
- }
- start++;
- }
- while (end > start) {
- const charCode = value.charCodeAt(end - 1);
- if (charCode !== 32 && charCode !== 9) {
- break;
- }
- end--;
- }
- return start === 0 && end === value.length ? value : value.slice(start, end);
- };
- var parse = (cookie, name) => {
- if (name && cookie.indexOf(name) === -1) {
- return {};
- }
- const pairs = cookie.split(";");
- const parsedCookie = {};
- for (const pairStr of pairs) {
- const valueStartPos = pairStr.indexOf("=");
- if (valueStartPos === -1) {
- continue;
- }
- const cookieName = trimCookieWhitespace(pairStr.substring(0, valueStartPos));
- if (name && name !== cookieName || !validCookieNameRegEx.test(cookieName)) {
- continue;
- }
- let cookieValue = trimCookieWhitespace(pairStr.substring(valueStartPos + 1));
- if (cookieValue.startsWith('"') && cookieValue.endsWith('"')) {
- cookieValue = cookieValue.slice(1, -1);
- }
- if (validCookieValueRegEx.test(cookieValue)) {
- parsedCookie[cookieName] = cookieValue.indexOf("%") !== -1 ? tryDecode(cookieValue, decodeURIComponent_) : cookieValue;
- if (name) {
- break;
- }
- }
- }
- return parsedCookie;
- };
- var parseSigned = async (cookie, secret, name) => {
- const parsedCookie = {};
- const secretKey = await getCryptoKey(secret);
- for (const [key, value] of Object.entries(parse(cookie, name))) {
- const signatureStartPos = value.lastIndexOf(".");
- if (signatureStartPos < 1) {
- continue;
- }
- const signedValue = value.substring(0, signatureStartPos);
- const signature = value.substring(signatureStartPos + 1);
- if (signature.length !== 44 || !signature.endsWith("=")) {
- continue;
- }
- const isVerified = await verifySignature(signature, signedValue, secretKey);
- parsedCookie[key] = isVerified ? signedValue : false;
- }
- return parsedCookie;
- };
- var _serialize = (name, value, opt = {}) => {
- if (!validCookieNameRegEx.test(name)) {
- throw new Error("Invalid cookie name");
- }
- let cookie = `${name}=${value}`;
- if (name.startsWith("__Secure-") && !opt.secure) {
- throw new Error("__Secure- Cookie must have Secure attributes");
- }
- if (name.startsWith("__Host-")) {
- if (!opt.secure) {
- throw new Error("__Host- Cookie must have Secure attributes");
- }
- if (opt.path !== "/") {
- throw new Error('__Host- Cookie must have Path attributes with "/"');
- }
- if (opt.domain) {
- throw new Error("__Host- Cookie must not have Domain attributes");
- }
- }
- for (const key of ["domain", "path"]) {
- if (opt[key] && /[;\r\n]/.test(opt[key])) {
- throw new Error(`${key} must not contain ";", "\\r", or "\\n"`);
- }
- }
- if (opt && typeof opt.maxAge === "number" && opt.maxAge >= 0) {
- if (opt.maxAge > 3456e4) {
- throw new Error(
- "Cookies Max-Age SHOULD NOT be greater than 400 days (34560000 seconds) in duration."
- );
- }
- cookie += `; Max-Age=${opt.maxAge | 0}`;
- }
- if (opt.domain && opt.prefix !== "host") {
- cookie += `; Domain=${opt.domain}`;
- }
- if (opt.path) {
- cookie += `; Path=${opt.path}`;
- }
- if (opt.expires) {
- if (opt.expires.getTime() - Date.now() > 3456e7) {
- throw new Error(
- "Cookies Expires SHOULD NOT be greater than 400 days (34560000 seconds) in the future."
- );
- }
- cookie += `; Expires=${opt.expires.toUTCString()}`;
- }
- if (opt.httpOnly) {
- cookie += "; HttpOnly";
- }
- if (opt.secure) {
- cookie += "; Secure";
- }
- if (opt.sameSite) {
- cookie += `; SameSite=${opt.sameSite.charAt(0).toUpperCase() + opt.sameSite.slice(1)}`;
- }
- if (opt.priority) {
- cookie += `; Priority=${opt.priority.charAt(0).toUpperCase() + opt.priority.slice(1)}`;
- }
- if (opt.partitioned) {
- if (!opt.secure) {
- throw new Error("Partitioned Cookie must have Secure attributes");
- }
- cookie += "; Partitioned";
- }
- return cookie;
- };
- var serialize = (name, value, opt) => {
- value = encodeURIComponent(value);
- return _serialize(name, value, opt);
- };
- var serializeSigned = async (name, value, secret, opt = {}) => {
- const signature = await makeSignature(value, secret);
- value = `${value}.${signature}`;
- value = encodeURIComponent(value);
- return _serialize(name, value, opt);
- };
- export {
- parse,
- parseSigned,
- serialize,
- serializeSigned
- };
|