mirror of
https://github.com/KeyZox71/knl_meowscendence.git
synced 2025-10-14 19:04:46 +02:00
「✨」 feat: added 2fa (closes #21)
This commit is contained in:
@ -16,10 +16,12 @@ if (!env || env === 'development') {
|
||||
*/
|
||||
function prepareDB() {
|
||||
database.exec(`
|
||||
CREATE TABLE IF NOT EXISTS credentials (
|
||||
username TEXT PRIMARY KEY,
|
||||
passwordHash TEXT
|
||||
) STRICT
|
||||
CREATE TABLE IF NOT EXISTS credentials (
|
||||
username TEXT PRIMARY KEY,
|
||||
passwordHash TEXT,
|
||||
totpHash TEXT DEFAULT NULL,
|
||||
totpEnabled INTEGER DEFAULT 0
|
||||
) STRICT
|
||||
`);
|
||||
}
|
||||
|
||||
@ -49,11 +51,48 @@ function passwordQuery(user) {
|
||||
return passwordQuery.get(user)
|
||||
}
|
||||
|
||||
function setTOTPSecret(user, secret) {
|
||||
let setTOTP = database.prepare('UPDATE credentials SET totpHash = ? WHERE username = ?');
|
||||
setTOTP.run(secret, user);
|
||||
}
|
||||
|
||||
function isTOTPEnabled(user) {
|
||||
const stmt = database.prepare('SELECT totpHash, totpEnabled FROM credentials WHERE username = ?');
|
||||
const result = stmt.get(user);
|
||||
return result && result.totpHash !== null && result.totpEnabled === 1;
|
||||
}
|
||||
|
||||
function disableTOTP(user) {
|
||||
let stmt = database.prepare('UPDATE credentials SET totpHash = NULL, totpEnabled = 0 WHERE username = ?');
|
||||
stmt.run(user);
|
||||
}
|
||||
|
||||
function queryTOTP(user) {
|
||||
let totpQuery = database.prepare('SELECT totpHash FROM credentials WHERE username = ?;');
|
||||
return totpQuery.get(user);
|
||||
}
|
||||
|
||||
function enableTOTP(user) {
|
||||
let stmt = database.prepare('UPDATE credentials SET totpEnabled = 1 WHERE username = ?');
|
||||
stmt.run(user);
|
||||
}
|
||||
|
||||
function getUser(user) {
|
||||
const stmt = database.prepare('SELECT * FROM credentials WHERE username = ?');
|
||||
return stmt.get(user);
|
||||
}
|
||||
|
||||
const authDB = {
|
||||
prepareDB,
|
||||
checkUser,
|
||||
addUser,
|
||||
passwordQuery,
|
||||
setTOTPSecret,
|
||||
isTOTPEnabled,
|
||||
disableTOTP,
|
||||
queryTOTP,
|
||||
enableTOTP,
|
||||
getUser,
|
||||
RESERVED_USERNAMES
|
||||
};
|
||||
|
||||
|
43
src/utils/totp.js
Normal file
43
src/utils/totp.js
Normal file
@ -0,0 +1,43 @@
|
||||
import { randomBytes, createHmac } from 'crypto';
|
||||
import base32 from 'base32.js';
|
||||
const { Decoder, Encoder } = base32;
|
||||
|
||||
const timeStep = 30;
|
||||
const T0 = 0;
|
||||
|
||||
export function generateRandomSecret() {
|
||||
const buf = randomBytes(20);
|
||||
return new Encoder().write(buf).finalize();
|
||||
}
|
||||
|
||||
export function getTimeCounter() {
|
||||
return Math.floor((Date.now() / 1000 - T0) / timeStep);
|
||||
}
|
||||
|
||||
export function verifyTOTP(base32Secret, userToken) {
|
||||
const window = 1;
|
||||
const decoder = new Decoder();
|
||||
const key = decoder.write(base32Secret).finalize();
|
||||
|
||||
const currentCounter = getTimeCounter();
|
||||
|
||||
for (let errorWindow = -window; errorWindow <= window; errorWindow++) {
|
||||
const counter = currentCounter + errorWindow;
|
||||
const generated = generateTOTP(key, counter);
|
||||
if (generated === userToken) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function generateTOTP(key, counter) {
|
||||
const buf = Buffer.alloc(8);
|
||||
buf.writeUInt32BE(0, 0);
|
||||
buf.writeUInt32BE(counter, 4);
|
||||
|
||||
const hmac = createHmac('sha1', key).update(buf).digest();
|
||||
|
||||
const offset = hmac[hmac.length - 1] & 0xf;
|
||||
const code = (hmac.readUInt32BE(offset) & 0x7fffffff) % 1_000_000;
|
||||
|
||||
return code.toString().padStart(6, '0');
|
||||
}
|
Reference in New Issue
Block a user