Email Verification & Password Reset
Email verification on sign-up and password reset flow.
Raypx sends verification emails on sign-up and supports a full password reset flow. Both features are powered by the @raypx/email package using React Email templates sent through Resend.
Email Verification
When a user signs up, a verification email is sent automatically. The configuration in packages/auth/src/index.ts:
emailVerification: {
autoSignInAfterVerification: true,
sendOnSignUp: true,
sendVerificationEmail: async ({ user, url }) => {
await sendVerificationEmail({ email: user.email, name: user.name, url })
},
},Key behaviors:
sendOnSignUp: true-- A verification email is sent immediately when a user registers.autoSignInAfterVerification: true-- After the user clicks the verification link, they are signed in automatically and redirected to the app.
Verification Flow
The user fills in name, email, and password. A new account is created in the user table with emailVerified set to false.
The sendVerificationEmail function in packages/auth/src/email.ts renders the template and sends it via Resend.
The link points to /api/auth/verify-email. Better Auth validates the token from the verification table.
The emailVerified field is set to true. Because autoSignInAfterVerification is enabled, the user is signed in and redirected.
Password Reset
The password reset flow allows users to reset their password via email.
Reset Configuration
emailAndPassword: {
resetPasswordTokenExpiresIn: 60 * 60, // 1 hour
revokeSessionsOnPasswordReset: true,
sendResetPassword: async ({ user, url }) => {
await sendPasswordResetEmail({ email: user.email, name: user.name, url })
},
onPasswordReset: async ({ user }) => {
logger.info("auth.password.reset", { feature: "auth", userId: user.id })
},
},Reset Flow
The user navigates to /forgot-password and enters their email address.
If the email exists in the database, a reset email is sent. The token expires in 1 hour.
The user clicks the link and is taken to /reset-password where they enter a new password.
All existing sessions are invalidated (revokeSessionsOnPasswordReset: true). The user must sign in again with the new password.
When revokeSessionsOnPasswordReset is true, all active sessions across all devices are terminated. This is a security measure to ensure that if a password was compromised, the attacker loses access immediately.
Email Templates
Templates live in packages/email/src/templates/ and follow the React Email component pattern:
Template Pattern
Each template exports an async function that returns { subject, html, text }:
import { Body, Button, Container, Head, Html, Preview, Section, Text } from "@react-email/components"
import { render } from "@react-email/render"
export type PasswordResetTemplateInput = {
name: string
url: string
}
export const passwordResetTemplate: AsyncEmailTemplate<PasswordResetTemplateInput> = async (input) => {
const { name, url } = input
const element = <PasswordResetEmail name={name} url={url} />
const html = await render(element, { pretty: false })
return {
subject: "Reset your password",
html,
text: `Hi ${name},\n\nWe received a request to reset your password...`,
}
}
function PasswordResetEmail({ name, url }: PasswordResetTemplateInput) {
return (
<Html>
<Head />
<Preview>Reset your password - {name}</Preview>
<Body style={main}>
<Container style={container}>
<Section style={header}>
<Text style={titleStyle}>Reset your password</Text>
</Section>
<Section style={bodySection}>
<Text style={textStyle}>Hi {name},</Text>
<Text style={textStyle}>
Click the button below to choose a new one:
</Text>
<Button href={url} style={buttonStyle}>Reset Password</Button>
</Section>
</Container>
</Body>
</Html>
)
}Customizing Templates
To customize the look and feel:
- Edit the inline styles in the template component (
main,container,buttonStyle, etc.). - Set
EMAIL_TEMPLATES_BASE_URLin your.envto show a "Sent from" footer. - Modify the
subjectand text fallback content.
Adding a New Template
- Create a new
.tsxfile inpackages/email/src/templates/. - Follow the same pattern: define input types, export an async template function.
- Export it from
packages/email/src/templates/index.ts. - Call it from the appropriate auth callback in
packages/auth/src/email.ts.
Email Sending
Emails are sent through the @raypx/email package, which supports multiple transports:
Set AUTH_RESEND_KEY (starts with re_) and RESEND_FROM. The Resend transport is used automatically when the key is present.
When no Resend key is set, emails are printed to the console via the logger transport. This is the default for local development.
A no-op transport that silently discards emails. Useful for testing.