Release v2.20.1: migrate authentication to Better Auth
This commit is contained in:
1 parent
e9a53f577c
commit
fba4f99ff7
92 files changed
+5085
-1943
No files matched your search
+43
-22
@@ -6,7 +6,7 @@ const multer = require('multer');
|
||||
const fs = require('fs');
|
||||
const crypto = require('crypto');
|
||||
const common = require('#src/common');
|
||||
const { verifyPassword, createSessionToken, hashSessionToken, hashPassword, validatePasswordStrength, createOneTimeToken } = require('#src/auth');
|
||||
const { verifyPassword, hashSessionToken, hashPassword, validatePasswordStrength, createOneTimeToken } = require('#src/auth');
|
||||
const pages = require('#src/web/pages/index');
|
||||
const registerMiddleware = require('#src/web/middleware');
|
||||
const registerNotFoundHandler = require('#src/web/middleware/not-found');
|
||||
@@ -24,6 +24,7 @@ const { rbacData } = require('#src/web/lib/auth/index');
|
||||
const { createPlayerActionService } = require('#src/web/lib/player-actions');
|
||||
const { isClientNameAvailable, withClientNameReservation } = require('#src/data/client-name-check');
|
||||
const { createSessionService, getRequestOrigin } = require('#src/web/lib/auth/index');
|
||||
const { createBetterAuth, getRoleTwoFactorAuthError } = require('#src/web/lib/auth/better-auth');
|
||||
const { fetchAppSettings } = require('#src/data/app-settings');
|
||||
const { sendAccountEmail } = require('#src/data/mailer');
|
||||
const { recordRequestAuditEvent } = require('#src/data/audit-log');
|
||||
@@ -51,6 +52,20 @@ async function start() {
|
||||
const server = http.createServer(app);
|
||||
const pool = common.createPool();
|
||||
const webConfig = createWebConfig();
|
||||
fs.mkdirSync(webConfig.mediaDir, { recursive: true });
|
||||
await ensureFontLibrary(webConfig.mediaDir);
|
||||
await common.ensureSchema(pool, { mediaDir: webConfig.mediaDir });
|
||||
await common.bootstrapDatabase(pool);
|
||||
const betterAuthService = await createBetterAuth(pool, {
|
||||
secret: process.env.AUTH_SECRET,
|
||||
baseURL: process.env.BETTER_AUTH_URL || process.env.WEB_PUBLIC_URL,
|
||||
sessionCookieName: webConfig.sessionCookieName,
|
||||
sendAccountEmail: function (settings, message) { return sendAccountEmail(settings, message); }
|
||||
});
|
||||
const betterAuth = betterAuthService.auth;
|
||||
const toNodeHandler = betterAuthService.toNodeHandler;
|
||||
const fromNodeHeaders = betterAuthService.fromNodeHeaders;
|
||||
const betterAuthHandler = toNodeHandler(betterAuth);
|
||||
|
||||
// Background services and request forwarding helpers.
|
||||
const backgroundTaskQueue = createBackgroundTaskQueue({
|
||||
@@ -99,30 +114,38 @@ async function start() {
|
||||
// Session and auth helpers for route registration.
|
||||
const broadcastDashboardState = webBootstrap.broadcastDashboardState;
|
||||
const sessionService = createSessionService({
|
||||
sessionCookieName: webConfig.sessionCookieName,
|
||||
sessionMaxAgeMs: webConfig.sessionMaxAgeMs,
|
||||
getConfiguredSessionMaxAgeMs: async function () {
|
||||
const settings = await fetchAppSettings(pool);
|
||||
return Number(settings['security.session_lifetime_days']) * 24 * 60 * 60 * 1000;
|
||||
},
|
||||
getConfiguredMaxActiveSessions: async function () {
|
||||
const settings = await fetchAppSettings(pool);
|
||||
return Number(settings['security.max_active_sessions']);
|
||||
},
|
||||
hashSessionToken: hashSessionToken,
|
||||
createSessionToken: createSessionToken
|
||||
sessionCookieName: webConfig.sessionCookieName
|
||||
});
|
||||
const parseCookies = sessionService.parseCookies;
|
||||
const createUserSession = sessionService.createUserSession;
|
||||
const clearSessionCookie = sessionService.clearSessionCookie;
|
||||
const setSessionCookie = sessionService.setSessionCookie;
|
||||
const getSessionMaxAgeMs = sessionService.getSessionMaxAgeMs;
|
||||
const setAuthMessageCookie = sessionService.setAuthMessageCookie;
|
||||
const consumeAuthMessageCookie = sessionService.consumeAuthMessageCookie;
|
||||
const loadCurrentUser = sessionService.loadCurrentUser;
|
||||
const loadCurrentUser = async function (pool, req) {
|
||||
const authSession = await betterAuth.api.getSession({ headers: fromNodeHeaders(req.headers) });
|
||||
if (!authSession || !authSession.session || !authSession.session.token) return null;
|
||||
req.betterAuthSession = authSession;
|
||||
return sessionService.loadCurrentUser(pool, req, authSession.session.token);
|
||||
};
|
||||
const requireAuth = sessionService.requireAuth;
|
||||
const startViewAsUser = sessionService.startViewAsUser;
|
||||
const stopViewAsUser = sessionService.stopViewAsUser;
|
||||
app.all('/api/auth/*splat', async function (req, res, next) {
|
||||
try {
|
||||
const currentUser = await loadCurrentUser(pool, req);
|
||||
const policyError = getRoleTwoFactorAuthError(currentUser, req.path);
|
||||
if (policyError) return res.status(403).json(policyError);
|
||||
const authPath = String(req.path || '');
|
||||
if (currentUser && currentUser.isViewingAs && (authPath.startsWith('/api/auth/two-factor/') || authPath.startsWith('/api/auth/passkey/'))) {
|
||||
return res.status(403).json({ message: 'Exit View as before changing authentication settings.' });
|
||||
}
|
||||
if (!req.headers['x-forwarded-for'] && req.socket && req.socket.remoteAddress) {
|
||||
req.headers['x-forwarded-for'] = req.socket.remoteAddress;
|
||||
}
|
||||
return betterAuthHandler(req, res, next);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
// Middleware and route registration.
|
||||
registerMiddleware(app, {
|
||||
pool: pool,
|
||||
@@ -137,16 +160,15 @@ async function start() {
|
||||
|
||||
registerRoutes(app, {
|
||||
pool: pool,
|
||||
betterAuth: betterAuth,
|
||||
fromNodeHeaders: fromNodeHeaders,
|
||||
common: common,
|
||||
pages: pages,
|
||||
upload: upload,
|
||||
mediaDir: webConfig.mediaDir,
|
||||
uploadDir: webConfig.uploadsDir,
|
||||
createUserSession: createUserSession,
|
||||
startViewAsUser: startViewAsUser,
|
||||
stopViewAsUser: stopViewAsUser,
|
||||
setSessionCookie: setSessionCookie,
|
||||
getSessionMaxAgeMs: getSessionMaxAgeMs,
|
||||
clearSessionCookie: clearSessionCookie,
|
||||
setAuthMessageCookie: setAuthMessageCookie,
|
||||
consumeAuthMessageCookie: consumeAuthMessageCookie,
|
||||
@@ -235,11 +257,10 @@ async function start() {
|
||||
});
|
||||
|
||||
// Bootstrap and startup tasks.
|
||||
fs.mkdirSync(webConfig.mediaDir, { recursive: true });
|
||||
await ensureFontLibrary(webConfig.mediaDir);
|
||||
await initializeWebServer({
|
||||
common: common,
|
||||
pool: pool,
|
||||
databaseInitialized: true,
|
||||
mediaDir: webConfig.mediaDir,
|
||||
backgroundTaskQueue: backgroundTaskQueue,
|
||||
webBootstrap: webBootstrap,
|
||||
|
||||
Reference in new issue
Block a user