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:
committed by
GitHub
parent
20f7fd2c22
commit
d31f3eb993
@@ -7,7 +7,7 @@ export function CtaButton(
|
||||
) {
|
||||
return (
|
||||
<Button
|
||||
className="w-full bg-[#000000] rounded text-white text-[16px] font-semibold no-underline text-center py-3"
|
||||
className="w-full rounded bg-[#000000] py-3 text-center text-[16px] font-semibold text-white no-underline"
|
||||
href={props.href}
|
||||
>
|
||||
{props.children}
|
||||
|
||||
@@ -3,9 +3,7 @@ import { Container, Section } from '@react-email/components';
|
||||
export function EmailHeader(props: React.PropsWithChildren) {
|
||||
return (
|
||||
<Container>
|
||||
<Section>
|
||||
{props.children}
|
||||
</Section>
|
||||
<Section>{props.children}</Section>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -54,29 +54,29 @@ export async function renderAccountDeleteEmail(props: Props) {
|
||||
</EmailHeader>
|
||||
|
||||
<EmailContent>
|
||||
<Text className="text-[14px] leading-[24px] text-black">
|
||||
<Text className="text-[16px] leading-[24px] text-[#242424]">
|
||||
{t(`${namespace}:hello`, {
|
||||
displayName: props.userDisplayName,
|
||||
})}
|
||||
</Text>
|
||||
|
||||
<Text className="text-[14px] leading-[24px] text-black">
|
||||
<Text className="text-[16px] leading-[24px] text-[#242424]">
|
||||
{t(`${namespace}:paragraph1`, {
|
||||
productName: props.productName,
|
||||
})}
|
||||
</Text>
|
||||
|
||||
<Text className="text-[14px] leading-[24px] text-black">
|
||||
<Text className="text-[16px] leading-[24px] text-[#242424]">
|
||||
{t(`${namespace}:paragraph2`)}
|
||||
</Text>
|
||||
|
||||
<Text className="text-[14px] leading-[24px] text-black">
|
||||
<Text className="text-[16px] leading-[24px] text-[#242424]">
|
||||
{t(`${namespace}:paragraph3`, {
|
||||
productName: props.productName,
|
||||
})}
|
||||
</Text>
|
||||
|
||||
<Text className="text-[14px] leading-[24px] text-black">
|
||||
<Text className="text-[16px] leading-[24px] text-[#242424]">
|
||||
{t(`${namespace}:paragraph4`, {
|
||||
productName: props.productName,
|
||||
})}
|
||||
|
||||
@@ -79,12 +79,12 @@ export async function renderInviteEmail(props: Props) {
|
||||
</EmailHeader>
|
||||
|
||||
<EmailContent>
|
||||
<Text className="text-[14px] leading-[24px] text-black">
|
||||
<Text className="text-[16px] leading-[24px] text-[#242424]">
|
||||
{hello}
|
||||
</Text>
|
||||
|
||||
<Text
|
||||
className="text-[14px] leading-[24px] text-black"
|
||||
className="text-[16px] leading-[24px] text-[#242424]"
|
||||
dangerouslySetInnerHTML={{ __html: mainText }}
|
||||
/>
|
||||
|
||||
@@ -107,7 +107,7 @@ export async function renderInviteEmail(props: Props) {
|
||||
<CtaButton href={props.link}>{joinTeam}</CtaButton>
|
||||
</Section>
|
||||
|
||||
<Text className="text-[14px] leading-[24px] text-black">
|
||||
<Text className="text-[16px] leading-[24px] text-[#242424]">
|
||||
{t(`${namespace}:copyPasteLink`)}{' '}
|
||||
<Link href={props.link} className="text-blue-600 no-underline">
|
||||
{props.link}
|
||||
|
||||
97
packages/email-templates/src/emails/otp.email.tsx
Normal file
97
packages/email-templates/src/emails/otp.email.tsx
Normal file
@@ -0,0 +1,97 @@
|
||||
import {
|
||||
Body,
|
||||
Button,
|
||||
Head,
|
||||
Html,
|
||||
Preview,
|
||||
Section,
|
||||
Tailwind,
|
||||
Text,
|
||||
render,
|
||||
} from '@react-email/components';
|
||||
|
||||
import { BodyStyle } from '../components/body-style';
|
||||
import { EmailContent } from '../components/content';
|
||||
import { EmailFooter } from '../components/footer';
|
||||
import { EmailHeader } from '../components/header';
|
||||
import { EmailHeading } from '../components/heading';
|
||||
import { EmailWrapper } from '../components/wrapper';
|
||||
import { initializeEmailI18n } from '../lib/i18n';
|
||||
|
||||
interface Props {
|
||||
otp: string;
|
||||
productName: string;
|
||||
language?: string;
|
||||
}
|
||||
|
||||
export async function renderOtpEmail(props: Props) {
|
||||
const namespace = 'otp-email';
|
||||
|
||||
const { t } = await initializeEmailI18n({
|
||||
language: props.language,
|
||||
namespace,
|
||||
});
|
||||
|
||||
const subject = t(`${namespace}:subject`, {
|
||||
productName: props.productName,
|
||||
});
|
||||
|
||||
const previewText = subject;
|
||||
|
||||
const heading = t(`${namespace}:heading`, {
|
||||
productName: props.productName,
|
||||
});
|
||||
|
||||
const otpText = t(`${namespace}:otpText`, {
|
||||
otp: props.otp,
|
||||
});
|
||||
|
||||
const mainText = t(`${namespace}:mainText`);
|
||||
const footerText = t(`${namespace}:footerText`);
|
||||
|
||||
const html = await render(
|
||||
<Html>
|
||||
<Head>
|
||||
<BodyStyle />
|
||||
</Head>
|
||||
|
||||
<Preview>{previewText}</Preview>
|
||||
|
||||
<Tailwind>
|
||||
<Body>
|
||||
<EmailWrapper>
|
||||
<EmailHeader>
|
||||
<EmailHeading>{heading}</EmailHeading>
|
||||
</EmailHeader>
|
||||
|
||||
<EmailContent>
|
||||
<Text className="text-[16px] text-[#242424]">{mainText}</Text>
|
||||
|
||||
<Text className="text-[16px] text-[#242424]">{otpText}</Text>
|
||||
|
||||
<Section className="mb-[16px] mt-[16px] text-center">
|
||||
<Button className={'w-full rounded bg-neutral-950 text-center'}>
|
||||
<Text className="text-[16px] font-medium font-semibold leading-[16px] text-white">
|
||||
{props.otp}
|
||||
</Text>
|
||||
</Button>
|
||||
</Section>
|
||||
|
||||
<Text
|
||||
className="text-[16px] text-[#242424]"
|
||||
dangerouslySetInnerHTML={{ __html: footerText }}
|
||||
/>
|
||||
</EmailContent>
|
||||
|
||||
<EmailFooter>{props.productName}</EmailFooter>
|
||||
</EmailWrapper>
|
||||
</Body>
|
||||
</Tailwind>
|
||||
</Html>,
|
||||
);
|
||||
|
||||
return {
|
||||
html,
|
||||
subject,
|
||||
};
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './emails/invite.email';
|
||||
export * from './emails/account-delete.email';
|
||||
export * from './emails/otp.email';
|
||||
|
||||
7
packages/email-templates/src/locales/en/otp-email.json
Normal file
7
packages/email-templates/src/locales/en/otp-email.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"subject": "One-time password for {{productName}}",
|
||||
"heading": "One-time password for {{productName}}",
|
||||
"otpText": "Your one-time password is: {{otp}}",
|
||||
"footerText": "Please enter the one-time password in the app to continue.",
|
||||
"mainText": "You're receiving this email because you need to verify your identity using a one-time password."
|
||||
}
|
||||
Reference in New Issue
Block a user