const test = require('node:test'); const assert = require('node:assert/strict'); require('../src/common'); const registerAuthRoutes = require('../src/web/routes/auth'); const registerAccountRoutes = require('../src/web/routes/admin/account'); function createApp() { const handlers = {}; return { handlers: handlers, get(path, ...routeHandlers) { handlers['GET ' + path] = routeHandlers; }, post(path, ...routeHandlers) { handlers['POST ' + path] = routeHandlers; } }; } function settingsQuery(sql) { return String(sql).includes('SELECT id, setting_key, setting_value FROM o_app_settings'); } test('forgot password requests a Better Auth OTP for a verified account', async () => { let requestedEmail = ''; let renderedResetPage = null; const app = createApp(); const pool = { async query(sql) { if (settingsQuery(sql)) return [[{ setting_key: 'email.smtp_enabled', setting_value: true }]]; if (String(sql).includes('SELECT id, email, email_verified FROM a_users')) { return [[{ id: 9, email: 'person@example.test', email_verified: true }]]; } return [[]]; } }; registerAuthRoutes(app, { pool: pool, pages: { renderResetPasswordPage(message, email) { renderedResetPage = { message: message, email: email }; return 'reset-page'; } }, betterAuth: { api: { async requestPasswordResetEmailOTP({ body }) { requestedEmail = body.email; } } }, parseCookies() { return {}; }, hashSessionToken(value) { return value; }, sessionCookieName: 'session' }); const response = { send(value) { this.body = value; return this; } }; await app.handlers['POST /forgot-password'][0]({ body: { identity: 'person' } }, response, (error) => { throw error; }); assert.equal(requestedEmail, 'person@example.test'); assert.equal(response.body, 'reset-page'); assert.equal(renderedResetPage.email, ''); assert.match(renderedResetPage.message, /reset code/); }); test('password reset verifies the OTP through Better Auth', async () => { let resetBody = null; const app = createApp(); const pool = { async query(sql) { return settingsQuery(sql) ? [[]] : [[]]; } }; registerAuthRoutes(app, { pool: pool, pages: { renderResetPasswordPage(message, email) { return message + ':' + email; }, renderForgotPasswordPage(message) { return message; } }, betterAuth: { api: { async resetPasswordEmailOTP({ body }) { resetBody = body; } } }, validatePasswordStrength() { return ''; }, parseCookies() { return {}; }, hashSessionToken(value) { return value; }, sessionCookieName: 'session' }); const response = { send(value) { this.body = value; return this; } }; await app.handlers['POST /reset-password'][0]({ body: { email: 'person@example.test', otp: '123456', password: 'NewPassword!123', confirm_password: 'NewPassword!123' } }, response, (error) => { throw error; }); assert.deepEqual(resetBody, { email: 'person@example.test', otp: '123456', password: 'NewPassword!123' }); assert.match(response.body, /Password updated/); }); test('pending email change verifies through Better Auth and syncs the app email', async () => { const calls = []; const app = createApp(); const pool = { async query(sql, params) { calls.push([String(sql), params]); const text = String(sql); if (settingsQuery(text)) return [[{ setting_key: 'email.smtp_enabled', setting_value: true }]]; if (text.includes('SELECT email, email_verified, pending_email FROM a_users')) { return [[{ email: 'old@example.test', pending_email: 'new@example.test' }]]; } if (text.includes('SELECT id, created_at, modified_at FROM a_users')) { return [[{ id: 7, created_at: new Date(), modified_at: new Date() }]]; } return [{ affectedRows: 1, insertId: 1 }]; } }; let verifiedBody = null; registerAccountRoutes(app, { pool: pool, common: {}, pages: {}, betterAuth: { api: { async changeEmailEmailOTP({ body, headers }) { verifiedBody = { body: body, headers: headers }; } } }, fromNodeHeaders(headers) { return { cookie: headers.cookie }; }, parseCookies() { return {}; }, sessionCookieName: 'session' }); const response = { redirect(url) { this.redirectedTo = url; } }; await app.handlers['POST /account/email/verify'][0]({ body: { otp: '654321' }, headers: { cookie: 'session=token' }, currentUser: { id: 7 } }, response, (error) => { throw error; }); assert.deepEqual(verifiedBody, { body: { newEmail: 'new@example.test', otp: '654321' }, headers: { cookie: 'session=token' } }); assert.ok(calls.some(([sql, params]) => sql.includes('UPDATE a_users SET email = ?, email_verified = 1, pending_email = NULL') && params[0] === 'new@example.test')); assert.match(response.redirectedTo, /Email%20address%20verified/); });