meilisearch
Version:
The Meilisearch JS client for Node.js and the browser.
1 lines • 7.65 kB
Source Map (JSON)
{"version":3,"file":"token.cjs","sources":["../../src/token.ts"],"sourcesContent":["import type {\n TenantTokenGeneratorOptions,\n TenantTokenHeader,\n TokenClaims,\n} from \"./types/index.js\";\n\nfunction getOptionsWithDefaults(options: TenantTokenGeneratorOptions) {\n const {\n searchRules = [\"*\"],\n algorithm = \"HS256\",\n force = false,\n ...restOfOptions\n } = options;\n return { searchRules, algorithm, force, ...restOfOptions };\n}\n\n// TODO: There's no point in this, or maybe even the above fn\ntype TenantTokenGeneratorOptionsWithDefaults = ReturnType<\n typeof getOptionsWithDefaults\n>;\n\nconst UUID_V4_REGEXP = /^[0-9a-f]{8}\\b(?:-[0-9a-f]{4}\\b){3}-[0-9a-f]{12}$/i;\nfunction isValidUUIDv4(uuid: string): boolean {\n return UUID_V4_REGEXP.test(uuid);\n}\n\nfunction encodeToBase64(data: unknown): string {\n // TODO: instead of btoa use Uint8Array.prototype.toBase64() when it becomes available in supported runtime versions\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/toBase64\n return btoa(typeof data === \"string\" ? data : JSON.stringify(data));\n}\n\nconst textEncoder = new TextEncoder();\n\n/** Create the signature of the token. */\nasync function sign(\n { apiKey, algorithm }: TenantTokenGeneratorOptionsWithDefaults,\n encodedPayload: string,\n encodedHeader: string,\n): Promise<string> {\n const cryptoKey = await crypto.subtle.importKey(\n // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey#raw\n \"raw\",\n textEncoder.encode(apiKey),\n // https://developer.mozilla.org/en-US/docs/Web/API/HmacImportParams#instance_properties\n { name: \"HMAC\", hash: `SHA-${algorithm.slice(2)}` },\n // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey#extractable\n false,\n // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey#keyusages\n [\"sign\"],\n );\n\n const signature = await crypto.subtle.sign(\n \"HMAC\",\n cryptoKey,\n textEncoder.encode(`${encodedHeader}.${encodedPayload}`),\n );\n\n // TODO: Same problem as in `encodeToBase64` above\n const digest = btoa(String.fromCharCode(...new Uint8Array(signature)))\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=/g, \"\");\n\n return digest;\n}\n\n/** Create the header of the token. */\nfunction getHeader({\n algorithm: alg,\n}: TenantTokenGeneratorOptionsWithDefaults): string {\n const header: TenantTokenHeader = { alg, typ: \"JWT\" };\n return encodeToBase64(header).replace(/=/g, \"\");\n}\n\n/** Create the payload of the token. */\nfunction getPayload({\n searchRules,\n apiKeyUid,\n expiresAt,\n}: TenantTokenGeneratorOptionsWithDefaults): string {\n if (!isValidUUIDv4(apiKeyUid)) {\n throw new Error(\"the uid of your key is not a valid UUIDv4\");\n }\n\n const payload: TokenClaims = { searchRules, apiKeyUid };\n if (expiresAt !== undefined) {\n payload.exp =\n typeof expiresAt === \"number\"\n ? expiresAt\n : // To get from a Date object the number of seconds since Unix epoch, i.e. Unix timestamp:\n Math.floor(expiresAt.getTime() / 1000);\n }\n\n return encodeToBase64(payload).replace(/=/g, \"\");\n}\n\n/**\n * Try to detect if the script is running in a server-side runtime.\n *\n * @remarks\n * There is no silver bullet way for determining the environment. Even so, this\n * is the recommended way according to\n * {@link https://min-common-api.proposal.wintercg.org/#navigator-useragent-requirements | WinterCG specs}.\n * {@link https://developer.mozilla.org/en-US/docs/Web/API/Navigator/userAgent | User agent }\n * can be spoofed, `process` can be patched. Even so it should prevent misuse\n * for the overwhelming majority of cases.\n */\nfunction tryDetectEnvironment(): void {\n if (typeof navigator !== \"undefined\" && \"userAgent\" in navigator) {\n const { userAgent } = navigator;\n\n if (\n userAgent.startsWith(\"Node\") ||\n userAgent.startsWith(\"Deno\") ||\n userAgent.startsWith(\"Bun\") ||\n userAgent.startsWith(\"Cloudflare-Workers\")\n ) {\n return;\n }\n }\n\n // Node.js prior to v21.1.0 doesn't have the above global\n // https://nodejs.org/api/globals.html#navigatoruseragent\n const versions = globalThis.process?.versions;\n if (versions !== undefined && Object.hasOwn(versions, \"node\")) {\n return;\n }\n\n throw new Error(\n \"failed to detect a server-side environment; do not generate tokens on the frontend in production!\\n\" +\n \"use the `force` option to disable environment detection, consult the documentation (Use at your own risk!)\",\n );\n}\n\n/**\n * Generate a tenant token.\n *\n * @remarks\n * Warning: while this can be used in browsers with\n * {@link TenantTokenGeneratorOptions.force}, it is only intended for server\n * side. Don't use this in production on the frontend, unless you really know\n * what you're doing!\n * @param options - Options object for tenant token generation\n * @returns The token in JWT (JSON Web Token) format\n * @see {@link https://www.meilisearch.com/docs/learn/security/basic_security | Securing your project}\n */\nexport async function generateTenantToken(\n options: TenantTokenGeneratorOptions,\n): Promise<string> {\n const optionsWithDefaults = getOptionsWithDefaults(options);\n\n if (!optionsWithDefaults.force) {\n tryDetectEnvironment();\n }\n\n const encodedPayload = getPayload(optionsWithDefaults);\n const encodedHeader = getHeader(optionsWithDefaults);\n const signature = await sign(\n optionsWithDefaults,\n encodedPayload,\n encodedHeader,\n );\n\n return `${encodedHeader}.${encodedPayload}.${signature}`;\n}\n"],"names":[],"mappings":";;AAMA,SAAS,uBAAuB,SAAsC;AAC9D,QAAA;AAAA,IACJ,cAAc,CAAC,GAAG;AAAA,IAClB,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,GAAG;AAAA,EAAA,IACD;AACJ,SAAO,EAAE,aAAa,WAAW,OAAO,GAAG,cAAc;AAC3D;AAOA,MAAM,iBAAiB;AACvB,SAAS,cAAc,MAAuB;AACrC,SAAA,eAAe,KAAK,IAAI;AACjC;AAEA,SAAS,eAAe,MAAuB;AAGtC,SAAA,KAAK,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,IAAI,CAAC;AACpE;AAEA,MAAM,cAAc,IAAI,YAAY;AAGpC,eAAe,KACb,EAAE,QAAQ,UAAU,GACpB,gBACA,eACiB;AACX,QAAA,YAAY,MAAM,OAAO,OAAO;AAAA;AAAA,IAEpC;AAAA,IACA,YAAY,OAAO,MAAM;AAAA;AAAA,IAEzB,EAAE,MAAM,QAAQ,MAAM,OAAO,UAAU,MAAM,CAAC,CAAC,GAAG;AAAA;AAAA,IAElD;AAAA;AAAA,IAEA,CAAC,MAAM;AAAA,EACT;AAEM,QAAA,YAAY,MAAM,OAAO,OAAO;AAAA,IACpC;AAAA,IACA;AAAA,IACA,YAAY,OAAO,GAAG,aAAa,IAAI,cAAc,EAAE;AAAA,EACzD;AAGM,QAAA,SAAS,KAAK,OAAO,aAAa,GAAG,IAAI,WAAW,SAAS,CAAC,CAAC,EAClE,QAAQ,OAAO,GAAG,EAClB,QAAQ,OAAO,GAAG,EAClB,QAAQ,MAAM,EAAE;AAEZ,SAAA;AACT;AAGA,SAAS,UAAU;AAAA,EACjB,WAAW;AACb,GAAoD;AAClD,QAAM,SAA4B,EAAE,KAAK,KAAK,MAAM;AACpD,SAAO,eAAe,MAAM,EAAE,QAAQ,MAAM,EAAE;AAChD;AAGA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AACF,GAAoD;AAC9C,MAAA,CAAC,cAAc,SAAS,GAAG;AACvB,UAAA,IAAI,MAAM,2CAA2C;AAAA,EAAA;AAGvD,QAAA,UAAuB,EAAE,aAAa,UAAU;AACtD,MAAI,cAAc,QAAW;AACnB,YAAA,MACN,OAAO,cAAc,WACjB;AAAA;AAAA,MAEA,KAAK,MAAM,UAAU,YAAY,GAAI;AAAA;AAAA,EAAA;AAG7C,SAAO,eAAe,OAAO,EAAE,QAAQ,MAAM,EAAE;AACjD;AAaA,SAAS,uBAA6B;AACpC,MAAI,OAAO,cAAc,eAAe,eAAe,WAAW;AAC1D,UAAA,EAAE,cAAc;AAEtB,QACE,UAAU,WAAW,MAAM,KAC3B,UAAU,WAAW,MAAM,KAC3B,UAAU,WAAW,KAAK,KAC1B,UAAU,WAAW,oBAAoB,GACzC;AACA;AAAA,IAAA;AAAA,EACF;AAKI,QAAA,WAAW,WAAW,SAAS;AACrC,MAAI,aAAa,UAAa,OAAO,OAAO,UAAU,MAAM,GAAG;AAC7D;AAAA,EAAA;AAGF,QAAM,IAAI;AAAA,IACR;AAAA,EAEF;AACF;AAcA,eAAsB,oBACpB,SACiB;AACX,QAAA,sBAAsB,uBAAuB,OAAO;AAEtD,MAAA,CAAC,oBAAoB,OAAO;AACT,yBAAA;AAAA,EAAA;AAGjB,QAAA,iBAAiB,WAAW,mBAAmB;AAC/C,QAAA,gBAAgB,UAAU,mBAAmB;AACnD,QAAM,YAAY,MAAM;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO,GAAG,aAAa,IAAI,cAAc,IAAI,SAAS;AACxD;;"}