@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
JavaScript
"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;