Implement DisableAuth and implement doubleCheckPassword()

This commit is contained in:
Louis Lam
2026-06-13 19:46:45 +08:00
parent 8d29fdec37
commit 7cce7df233
4 changed files with 58 additions and 43 deletions
+53 -15
View File
@@ -1,4 +1,4 @@
import { betterAuth } from "better-auth";
import { betterAuth, Session } from "better-auth";
// @ts-ignore
import * as Database from "./database.js";
import { genSecret, log } from "../src/util";
@@ -123,32 +123,51 @@ export function getAuthSecret() {
export function getSession(cookie: string) {
log.info("auth", "Logged in with httpOnly cookie session");
const context = {
headers: new Headers(),
headers: createHeaders(cookie),
};
context.headers.set("cookie", cookie || "");
return authInstance.api.getSession(context);
}
/**
*
* Create Headers object with cookie for API calls
* @param cookie Cookie string
* @returns Headers object
*/
export function createHeaders(cookie: string) {
const headers = new Headers();
headers.set("cookie", cookie || "");
return headers;
}
/**
* Unfortunatety, there is no way to get a user object from better-auth api, so we have to craft a session object here.
* @returns Crafted Session Object
*/
export async function getDisableAuthSession(): ReturnType<typeof getSession> {
log.info("auth", "Logged in with Disable Auth");
const { users, total } = await authInstance.api.listUsers({
query: {
limit: 1,
},
});
const obj = await R.getRow("SELECT * FROM better_auth_user LIMIT 1");
if (total == 0) {
throw new Error("Unexpected error. No users found");
if (!obj) {
throw new Error("Unexpected Error: No user found in the database.");
}
// Seems bugged, user from listUsers does not have all properties
// Force parse the user object to BetterAuthUser.
// https://github.com/better-auth/better-auth/issues/7452
const user = users[0] as BetterAuthUser;
const user = {
id: obj.id as string,
createdAt: obj.createdAt as Date,
updatedAt: obj.updatedAt as Date,
email: obj.email as string,
emailVerified: obj.emailVerified === 1,
name: obj.name as string,
image: obj.image as string | null,
username: obj.username as string | null,
displayUsername: obj.displayUsername as string | null,
banned: obj.banned === 1,
role: obj.role as string | null,
banReason: obj.banReason as string | null,
banExpires: obj.banExpires as Date | null,
twoFactorEnabled: obj.twoFactorEnabled === 1,
};
return {
user,
@@ -177,6 +196,25 @@ export function checkLogin(socket: Socket) {
}
}
/**
* For logged-in users, double-check the password
* @param cookie Cookie string
* @param currentPassword Password to verify
* @throws Error if the password is incorrect or the user is not found
*/
export async function doubleCheckPassword(cookie: string, currentPassword: string): Promise<void> {
const { status } = await authInstance.api.verifyPassword({
body: {
password: currentPassword,
},
headers: createHeaders(cookie),
});
if (!status) {
throw new Error("Incorrect current password");
}
}
/**
* TODO
*/
+2 -3
View File
@@ -4,7 +4,7 @@
* DO NOT require("./server") in other modules, it likely creates circular dependency!
*/
import { getRandomInt, isDev, log, sleep } from "../src/util";
import { auth, getDisableAuthSession, getSession } from "./better-auth";
import { auth, doubleCheckPassword, getDisableAuthSession, getSession } from "./better-auth";
import { createBetterAuthRouter, needSetup } from "./routers/better-auth-router";
import { betterAuthSocketHandler } from "./socket-handlers/better-auth-socket-handler";
import { loadEnvFile } from "node:process";
@@ -108,7 +108,6 @@ const {
setSettings,
setting,
checkLogin,
doubleCheckPassword,
allowDevAllOrigin,
printServerUrls,
allowDevOrigin,
@@ -1118,7 +1117,7 @@ app.use(function (req, res, next) {
// Enabled Auth + Want to Enable Auth => No Check
const currentDisabledAuth = await setting("disableAuth");
if (!currentDisabledAuth && data.disableAuth) {
await doubleCheckPassword(socket, currentPassword);
await doubleCheckPassword(socket.request.headers.cookie, currentPassword);
}
// Log out all clients if enabling auth
@@ -1,7 +1,8 @@
const { checkLogin, setSetting, setting, doubleCheckPassword } = require("../util-server");
const { checkLogin, setSetting, setting } = require("../util-server");
const { CloudflaredTunnel } = require("node-cloudflared-tunnel");
const { UptimeKumaServer } = require("../uptime-kuma-server");
const { log } = require("../../src/util");
const { doubleCheckPassword } = require("../better-auth");
const io = UptimeKumaServer.getInstance().io;
const prefix = "cloudflared_";
@@ -74,7 +75,7 @@ module.exports.cloudflaredSocketHandler = (socket) => {
checkLogin(socket);
const disabledAuth = await setting("disableAuth");
if (!disabledAuth) {
await doubleCheckPassword(socket, currentPassword);
await doubleCheckPassword(socket.request.headers.cookie, currentPassword);
}
cloudflared.stop();
} catch (error) {
-23
View File
@@ -10,7 +10,6 @@ const {
PING_COUNT_DEFAULT,
PING_PER_REQUEST_TIMEOUT_DEFAULT,
} = require("../src/util");
const passwordHash = require("./password-hash");
const iconv = require("iconv-lite");
const chardet = require("chardet");
const chroma = require("chroma-js");
@@ -650,28 +649,6 @@ exports.checkLogin = (socket) => {
betterAuthCheckLogin(socket);
};
/**
* For logged-in users, double-check the password
* @param {Socket} socket Socket.io instance
* @param {string} currentPassword Password to validate
* @returns {Promise<Bean>} User
* @throws The current password is not a string
* @throws The provided password is not correct
*/
exports.doubleCheckPassword = async (socket, currentPassword) => {
if (typeof currentPassword !== "string") {
throw new Error("Wrong data type?");
}
let user = await R.findOne("user", " id = ? AND active = 1 ", [socket.userID]);
if (!user || !passwordHash.verify(currentPassword, user.password)) {
throw new Error("Incorrect current password");
}
return user;
};
/**
* Convert unknown string to UTF8
* @param {Uint8Array} body Buffer