UNPKG

@dr.pogodin/csurf

Version:

CSRF token middleware for ExpressJS

102 lines (96 loc) 2.96 kB
"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