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
@@ -0,0 +1,137 @@
|
||||
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/);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user