UNPKG

@perfood/couch-auth

Version:

Easy and secure authentication for CouchDB/Cloudant. Based on SuperLogin, updated and rewritten in Typescript.

101 lines (100 loc) 3.67 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Hashing = void 0; const couch_pwd_1 = __importDefault(require("@sl-nx/couch-pwd")); const util_1 = require("./util"); const pwd = { 'sha1': new couch_pwd_1.default(), 'sha256': new couch_pwd_1.default(600000, 20, 16, 'hex', 'sha256') }; class Hashing { constructor(config) { this.hashers = { 'sha1': [], 'sha256': [] }; this.times = { 'sha1': [], 'sha256': [] }; this.defaultDigest = 'sha256'; this.dummyHashObject = { iterations: 10 }; this.defaultDigest = config.security?.defaultDigest || 'sha256'; const iterationPairs = config.security?.iterations; if (iterationPairs) { for (const pair of config.security.iterations) { const digest = pair[2] || 'sha1'; // assume existing config to refer to sha1 this.times[digest].push(pair[0]); this.hashers[digest].push(new couch_pwd_1.default(pair[1], 20, 16, 'hex', digest)); } } this.hashUserPassword((0, util_1.URLSafeUUID)()).then(dummy => { this.dummyHashObject = dummy; }); } getHasherForTimestamp(ts = undefined, digest) { const digestType = digest || this.defaultDigest; let ret = pwd[digestType]; if (this.times[digestType].length === 0 || ts === undefined) { return ret; } for (let idx = 0; idx < this.times[digestType].length; idx++) { if (ts > this.times[digestType][idx]) { ret = this.hashers[digestType][idx]; } else { break; } } return ret; } hashUserPassword(pw) { const t = new Date().valueOf(); return new Promise((resolve, reject) => { const hasher = this.getHasherForTimestamp(t); hasher.hash(pw, (err, salt, hash) => { if (err) { return reject(err); } return resolve({ salt: salt, derived_key: hash, password_scheme: 'pbkdf2', pbkdf2_prf: hasher.digest, iterations: hasher.iterations }); }); }).then((hr) => { hr.created = t; return hr; }); } verifyUserPassword(hashObj, pw) { const salt = hashObj.salt ?? this.dummyHashObject.salt; const derived_key = hashObj.derived_key ?? this.dummyHashObject.derived_key; const digest = this.getDigest(hashObj.pbkdf2_prf); let created = hashObj.created; if (!hashObj.salt && !hashObj.derived_key) { created = this.dummyHashObject.created; } return new Promise((resolve, reject) => { const hasher = this.getHasherForTimestamp(created, digest); hasher.hash(pw, salt, (err, hash) => { if (err) { return reject(err); } else if (hash !== derived_key) { return reject(false); } else { return resolve(true); } }); }); } getDigest(pbkdf2_prf) { if (pbkdf2_prf === 'sha256') { return 'sha256'; } // treat missing pbkdf2_prf as 'sha1' for backward compatibility return 'sha1'; } } exports.Hashing = Hashing;