61 lines
2.0 KiB
JavaScript
61 lines
2.0 KiB
JavaScript
// Password hashing and session token helpers.
|
|
|
|
const crypto = require('crypto');
|
|
|
|
const PASSWORD_ITERATIONS = Number(process.env.PASSWORD_HASH_ITERATIONS || 310000);
|
|
const PASSWORD_KEY_LENGTH = 32;
|
|
const PASSWORD_DIGEST = 'sha256';
|
|
const SESSION_BYTES = 32;
|
|
|
|
function validatePasswordStrength(password) {
|
|
const value = String(password || '');
|
|
const hasLowercase = /[a-z]/.test(value);
|
|
const hasUppercase = /[A-Z]/.test(value);
|
|
const hasNumber = /[0-9]/.test(value);
|
|
const hasSymbol = /[^A-Za-z0-9]/.test(value);
|
|
const categoryCount = [hasLowercase, hasUppercase, hasNumber, hasSymbol].filter(Boolean).length;
|
|
|
|
if (value.length < 10 || categoryCount < 3) {
|
|
return 'Password must be at least 10 characters and include 3 of: uppercase, lowercase, number, and symbol.';
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
function hashPassword(password, salt) {
|
|
const safePassword = String(password || '');
|
|
const safeSalt = salt || crypto.randomBytes(16).toString('hex');
|
|
const hash = crypto.pbkdf2Sync(safePassword, safeSalt, PASSWORD_ITERATIONS, PASSWORD_KEY_LENGTH, PASSWORD_DIGEST).toString('hex');
|
|
return {
|
|
salt: safeSalt,
|
|
hash: hash,
|
|
iterations: PASSWORD_ITERATIONS
|
|
};
|
|
}
|
|
|
|
function verifyPassword(password, record) {
|
|
if (!record || !record.password_salt || !record.password_hash) {
|
|
return false;
|
|
}
|
|
const iterations = Number(record.password_iterations || PASSWORD_ITERATIONS);
|
|
const safePassword = String(password || '');
|
|
const expected = crypto.pbkdf2Sync(safePassword, String(record.password_salt), iterations, PASSWORD_KEY_LENGTH, PASSWORD_DIGEST);
|
|
const actual = Buffer.from(String(record.password_hash), 'hex');
|
|
return actual.length === expected.length && crypto.timingSafeEqual(actual, expected);
|
|
}
|
|
|
|
function createSessionToken() {
|
|
return crypto.randomBytes(SESSION_BYTES).toString('hex');
|
|
}
|
|
|
|
function hashSessionToken(token) {
|
|
return crypto.createHash('sha256').update(String(token || '')).digest('hex');
|
|
}
|
|
|
|
module.exports = {
|
|
hashPassword,
|
|
verifyPassword,
|
|
validatePasswordStrength,
|
|
createSessionToken,
|
|
hashSessionToken
|
|
}; |