mirror of
https://github.com/abort-retry-ignore/joplock.git
synced 2026-05-22 19:57:34 +00:00
62 lines
1.4 KiB
JavaScript
62 lines
1.4 KiB
JavaScript
'use strict';
|
|
|
|
const crypto = require('crypto');
|
|
|
|
const RECOVERY_COOKIE = 'joplockRecoverySession';
|
|
|
|
const timingSafeEqual = (a, b) => {
|
|
const left = Buffer.from(`${a || ''}`);
|
|
const right = Buffer.from(`${b || ''}`);
|
|
if (left.length !== right.length) return false;
|
|
return crypto.timingSafeEqual(left, right);
|
|
};
|
|
|
|
const createRecoveryService = options => {
|
|
const {
|
|
enabled = false,
|
|
password = '',
|
|
sessionTtlMinutes = 30,
|
|
now = () => Date.now(),
|
|
} = options || {};
|
|
|
|
const sessions = new Map();
|
|
|
|
const isEnabled = () => !!enabled && !!password;
|
|
const ttlMs = Math.max(1, Number(sessionTtlMinutes || 30)) * 60 * 1000;
|
|
|
|
const createSession = attempt => {
|
|
if (!isEnabled()) throw new Error('Recovery mode is disabled');
|
|
if (!timingSafeEqual(attempt, password)) return '';
|
|
const token = crypto.randomBytes(24).toString('hex');
|
|
sessions.set(token, now() + ttlMs);
|
|
return token;
|
|
};
|
|
|
|
const validateSession = token => {
|
|
if (!isEnabled() || !token) return false;
|
|
const expiresAt = sessions.get(token);
|
|
if (!expiresAt) return false;
|
|
if (expiresAt <= now()) {
|
|
sessions.delete(token);
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
const endSession = token => {
|
|
if (token) sessions.delete(token);
|
|
};
|
|
|
|
return {
|
|
RECOVERY_COOKIE,
|
|
isEnabled,
|
|
createSession,
|
|
validateSession,
|
|
endSession,
|
|
};
|
|
};
|
|
|
|
module.exports = {
|
|
RECOVERY_COOKIE,
|
|
createRecoveryService,
|
|
};
|