{"version":3,"file":"tokens.mjs","names":["crypto","rndm","compare","uid","EQUAL_GLOBAL_REGEXP","PLUS_GLOBAL_REGEXP","SLASH_GLOBAL_REGEXP","hash","str","createHash","update","digest","replace","privateTokenize","secret","salt","verify","token","index","indexOf","slice","expected","Tokens","constructor","options","opts","saltLength","Number","isFinite","TypeError","secretLength","create","callback","undefined","secretSync","sync"],"sources":["../../src/tokens.ts"],"sourcesContent":["import crypto from 'node:crypto';\n\nimport rndm from 'rndm';\nimport compare from 'tsscmp';\nimport uid from 'uid-safe';\n\nconst EQUAL_GLOBAL_REGEXP = /=/g;\nconst PLUS_GLOBAL_REGEXP = /\\+/g;\nconst SLASH_GLOBAL_REGEXP = /\\//g;\n\n/**\n * Hash a string with SHA256, returning url-safe base64.\n */\nfunction hash(str: string): string {\n  return crypto\n    .createHash('sha256')\n    .update(str, 'ascii')\n    .digest('base64')\n    .replace(PLUS_GLOBAL_REGEXP, '-')\n    .replace(SLASH_GLOBAL_REGEXP, '_')\n    .replace(EQUAL_GLOBAL_REGEXP, '');\n}\n\n/**\n * Tokenize a secret and salt.\n */\nfunction privateTokenize(secret: string, salt: string): string {\n  return `${salt}-${hash(`${salt}-${secret}`)}`;\n}\n\n/**\n * Verify if a given token is valid for a given secret.\n */\nexport function verify(secret: string, token: string): boolean {\n  if (!secret || typeof secret !== 'string') {\n    return false;\n  }\n\n  if (!token || typeof token !== 'string') {\n    return false;\n  }\n\n  const index = token.indexOf('-');\n\n  if (index === -1) {\n    return false;\n  }\n\n  const salt = token.slice(0, index);\n  const expected = privateTokenize(secret, salt);\n\n  return compare(token, expected);\n}\n\nexport type Options = {\n  saltLength?: number;\n  secretLength?: number;\n};\n\n/**\n * Token generation/verification class.\n */\nexport default class Tokens {\n  private saltLength: number;\n  private secretLength: number;\n\n  /**\n   * @param [options]\n   * @param [options.saltLength=8] The string length of the salt\n   * @param [options.secretLength=18] The byte length of the secret key\n   */\n  constructor(options?: Options) {\n    const opts = options ?? {};\n\n    const saltLength = opts.saltLength ?? 8;\n\n    if (typeof saltLength !== 'number' || !Number.isFinite(saltLength) || saltLength < 1) {\n      throw new TypeError('option saltLength must be finite number > 1');\n    }\n\n    const secretLength = opts.secretLength ?? 18;\n\n    if (typeof secretLength !== 'number' || !Number.isFinite(secretLength) || secretLength < 1) {\n      throw new TypeError('option secretLength must be finite number > 1');\n    }\n\n    this.saltLength = saltLength;\n    this.secretLength = secretLength;\n  }\n\n  /**\n   * Create a new CSRF token.\n   *\n   * @param secret The secret for the token.\n   */\n  create(secret: string): string {\n    if (!secret || typeof secret !== 'string') {\n      throw new TypeError('argument secret is required');\n    }\n\n    return privateTokenize(secret, rndm(this.saltLength));\n  }\n\n  secret(): Promise<string>;\n  secret(callback: (err: unknown, str: string) => void): void;\n\n  /**\n   * Create a new secret key.\n   */\n  secret(\n    callback?: (err: unknown, str: string) => void,\n  ): Promise<string> | undefined {\n    if (callback) {\n      uid(this.secretLength, callback);\n      return undefined;\n    }\n\n    return uid(this.secretLength);\n  }\n\n  /**\n   * Create a new secret key synchronously.\n   */\n  secretSync(): string {\n    return uid.sync(this.secretLength);\n  }\n}\n"],"mappings":"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","ignoreList":[]}