UNPKG

hfs

Version:
88 lines (87 loc) 4.28 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.invalidateSessionBefore = void 0; exports.srpServerStep1 = srpServerStep1; exports.srpCheck = srpCheck; exports.getCurrentUsername = getCurrentUsername; exports.clearTextLogin = clearTextLogin; exports.setLoggedIn = setLoggedIn; const perm_1 = require("./perm"); const cross_const_1 = require("./cross-const"); const tssrp6a_1 = require("tssrp6a"); const srp_1 = require("./srp"); const cross_1 = require("./cross"); const expiringCache_1 = require("./expiringCache"); const node_crypto_1 = require("node:crypto"); const events_1 = __importDefault(require("./events")); const srp6aNimbusRoutines = new tssrp6a_1.SRPRoutines(new tssrp6a_1.SRPParameters()); async function srpServerStep1(account) { if (!account.srp) throw cross_const_1.HTTP_NOT_ACCEPTABLE; const [salt, verifier] = account.srp.split('|'); if (!salt || !verifier) throw Error("malformed account"); const srpSession = new tssrp6a_1.SRPServerSession(srp6aNimbusRoutines); const srpServer = await srpSession.step1(account.username, BigInt(salt), BigInt(verifier)); return { srpServer, salt, pubKey: String(srpServer.B) }; // cast to string cause bigint can't be jsonized } const cache = (0, expiringCache_1.expiringCache)(60000); async function srpCheck(username, password) { const account = (0, perm_1.getAccount)(username); if (!(account === null || account === void 0 ? void 0 : account.srp) || !password) return; const k = (0, node_crypto_1.createHash)('sha256').update(username + password + account.srp).digest("hex"); const good = await cache.try(k, async () => { const { srpServer, salt, pubKey } = await srpServerStep1(account); const client = await (0, srp_1.srpClientPart)(username, password, salt, pubKey); return srpServer.step2(client.A, client.M1).then(() => true, () => false); }); return good ? account : undefined; } function getCurrentUsername(ctx) { var _a; return ((_a = ctx.state.account) === null || _a === void 0 ? void 0 : _a.username) || ''; } async function clearTextLogin(ctx, u, p, via) { var _a; if ((_a = (await events_1.default.emitAsync('attemptingLogin', { ctx, username: u, via }))) === null || _a === void 0 ? void 0 : _a.isDefaultPrevented()) return; const plugins = await events_1.default.emitAsync('clearTextLogin', { ctx, username: u, password: p, via }); // provide clear password to plugins const a = (plugins === null || plugins === void 0 ? void 0 : plugins.some(x => x === true)) ? (0, perm_1.getAccount)(u) : await srpCheck(u, p); if (a) { await setLoggedIn(ctx, a.username); ctx.headers['x-username'] = a.username; // give an easier way to determine if the login was successful } else if (u) events_1.default.emit('failedLogin', { ctx, username: u, via }); return a; } // centralized log-in state async function setLoggedIn(ctx, username) { var _a; const s = ctx.session; if (!s) return ctx.throw(cross_const_1.HTTP_SERVER_ERROR, 'session'); if (username === false) { events_1.default.emit('logout', ctx); delete s.username; delete s.allowNet; return; } const a = ctx.state.account = (0, perm_1.getAccount)(username); if (!a) return; await events_1.default.emitAsync('finalizingLogin', { ctx, username, inputs: { ...ctx.state.params, ...ctx.query } }); s.username = (0, perm_1.normalizeUsername)(username); s.ts = Date.now(); const k = cross_const_1.ALLOW_SESSION_IP_CHANGE; s[k] = k in ctx.query || Boolean((_a = ctx.state.params) === null || _a === void 0 ? void 0 : _a[k]) || undefined; // login APIs will get ctx.state.params, others can rely on ctx.query if (!a.expire && a.days_to_live) (0, perm_1.updateAccount)(a, { expire: new Date(Date.now() + a.days_to_live * cross_1.DAY) }); await events_1.default.emitAsync('login', ctx); } // since session are currently stored in cookies, we need to store this information exports.invalidateSessionBefore = new Map();