Add support for OTPs and enhance sensitive apis with OTP verification (#191)

One-Time Password (OTP) package added with comprehensive token management, including OTP verification for team account deletion and ownership transfer.
This commit is contained in:
Giancarlo Buomprisco
2025-03-01 16:35:09 +07:00
committed by GitHub
parent 20f7fd2c22
commit d31f3eb993
60 changed files with 3543 additions and 1363 deletions

View File

@@ -8,6 +8,7 @@ export class Mailbox {
email: string,
params: {
deleteAfter: boolean;
subject?: string;
},
) {
const mailbox = email.split('@')[0];
@@ -18,13 +19,17 @@ export class Mailbox {
throw new Error('Invalid email');
}
const json = await this.getInviteEmail(mailbox, params);
const json = await this.getEmail(mailbox, params);
if (!json?.body) {
throw new Error('Email body was not found');
}
console.log('Email found');
console.log(`Email found for ${email}`, {
id: json.id,
subject: json.subject,
date: json.date,
});
const html = (json.body as { html: string }).html;
const el = parse(html);
@@ -40,10 +45,49 @@ export class Mailbox {
return this.page.goto(linkHref);
}
async getInviteEmail(
/**
* Retrieves an OTP code from an email
* @param email The email address to check for the OTP
* @param deleteAfter Whether to delete the email after retrieving the OTP
* @returns The OTP code
*/
async getOtpFromEmail(email: string, deleteAfter: boolean = true) {
const mailbox = email.split('@')[0];
console.log(`Retrieving OTP from mailbox ${email} ...`);
if (!mailbox) {
throw new Error('Invalid email');
}
const json = await this.getEmail(mailbox, {
deleteAfter,
subject: `One-time password for Makerkit`,
});
if (!json?.body) {
throw new Error('Email body was not found');
}
const html = (json.body as { html: string }).html;
const text = html.match(
new RegExp(`Your one-time password is: (\\d{6})`),
)?.[1];
if (text) {
console.log(`OTP code found in text: ${text}`);
return text;
}
throw new Error('Could not find OTP code in email');
}
async getEmail(
mailbox: string,
params: {
deleteAfter: boolean;
subject?: string;
},
) {
const url = `http://127.0.0.1:54324/api/v1/mailbox/${mailbox}`;
@@ -54,13 +98,34 @@ export class Mailbox {
throw new Error(`Failed to fetch emails: ${response.statusText}`);
}
const json = (await response.json()) as Array<{ id: string }>;
const json = (await response.json()) as Array<{
id: string;
subject: string;
}>;
if (!json || !json.length) {
console.log(`No emails found for mailbox ${mailbox}`);
return;
}
const messageId = json[0]?.id;
const message = params.subject
? (() => {
const filtered = json.filter(
(item) => item.subject === params.subject,
);
console.log(
`Found ${filtered.length} emails with subject ${params.subject}`,
);
return filtered[filtered.length - 1];
})()
: json[0];
console.log(`Message: ${JSON.stringify(message)}`);
const messageId = message?.id;
const messageUrl = `${url}/${messageId}`;
const messageResponse = await fetch(messageUrl);