"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
exports.verify = verify;
var _nodeCrypto = _interopRequireDefault(require("node:crypto"));
var _rndm = _interopRequireDefault(require("rndm"));
var _tsscmp = _interopRequireDefault(require("tsscmp"));
var _uidSafe = _interopRequireDefault(require("uid-safe"));
const EQUAL_GLOBAL_REGEXP = /=/g;
const PLUS_GLOBAL_REGEXP = /\+/g;
const SLASH_GLOBAL_REGEXP = /\//g;

/**
 * Hash a string with SHA256, returning url-safe base64.
 */
function hash(str) {
  return _nodeCrypto.default.createHash('sha256').update(str, 'ascii').digest('base64').replace(PLUS_GLOBAL_REGEXP, '-').replace(SLASH_GLOBAL_REGEXP, '_').replace(EQUAL_GLOBAL_REGEXP, '');
}

/**
 * Tokenize a secret and salt.
 */
function privateTokenize(secret, salt) {
  return `${salt}-${hash(`${salt}-${secret}`)}`;
}

/**
 * Verify if a given token is valid for a given secret.
 */
function verify(secret, token) {
  if (!secret || typeof secret !== 'string') {
    return false;
  }
  if (!token || typeof token !== 'string') {
    return false;
  }
  const index = token.indexOf('-');
  if (index === -1) {
    return false;
  }
  const salt = token.slice(0, index);
  const expected = privateTokenize(secret, salt);
  return (0, _tsscmp.default)(token, expected);
}
/**
 * Token generation/verification class.
 */
class Tokens {
  /**
   * @param [options]
   * @param [options.saltLength=8] The string length of the salt
   * @param [options.secretLength=18] The byte length of the secret key
   */
  constructor(options) {
    const opts = options ?? {};
    const saltLength = opts.saltLength ?? 8;
    if (typeof saltLength !== 'number' || !Number.isFinite(saltLength) || saltLength < 1) {
      throw new TypeError('option saltLength must be finite number > 1');
    }
    const secretLength = opts.secretLength ?? 18;
    if (typeof secretLength !== 'number' || !Number.isFinite(secretLength) || secretLength < 1) {
      throw new TypeError('option secretLength must be finite number > 1');
    }
    this.saltLength = saltLength;
    this.secretLength = secretLength;
  }

  /**
   * Create a new CSRF token.
   *
   * @param secret The secret for the token.
   */
  create(secret) {
    if (!secret || typeof secret !== 'string') {
      throw new TypeError('argument secret is required');
    }
    return privateTokenize(secret, (0, _rndm.default)(this.saltLength));
  }
  /**
   * Create a new secret key.
   */
  secret(callback) {
    if (callback) {
      (0, _uidSafe.default)(this.secretLength, callback);
      return undefined;
    }
    return (0, _uidSafe.default)(this.secretLength);
  }

  /**
   * Create a new secret key synchronously.
   */
  secretSync() {
    return _uidSafe.default.sync(this.secretLength);
  }
}
exports.default = Tokens;
//# sourceMappingURL=tokens.js.map