Email Authentication Setup with Resend
This guide explains how to set up email authentication using Resend with Convex Auth.
Prerequisites
- Create a Resend account
- Generate an API key from your Resend dashboard
- Verify your sending domain (or use Resend's test domain for development)
Configuration
1. Environment Variables
Add these to your .env.local file:
# Resend API Key
RESEND_API_KEY="re_xxxxxxxxxxxx"
# From email address (must be verified in Resend)
AUTH_RESEND_FROM="PromptNow <noreply@yourdomain.com>"
# Your site URL (for email links)
SITE_URL="https://yourdomain.com"2. Features Enabled
With the current setup, you get:
- Email Verification on Sign Up: Users receive a 6-digit code to verify their email
- Password Reset: Users can reset their password via email with a 6-digit code
- Welcome Emails: New users receive a welcome email after registration
- Password Change Notifications: Users are notified when their password changes
Email Templates
The email templates are defined in:
/convex/ResendOTP.ts- Verification and password reset emails/convex/emails.ts- Welcome and notification emails
Customizing Email Templates
You can customize the email templates by modifying the HTML in the respective files:
// Example: Customize the verification email
async sendVerificationRequest({ identifier: email, token, provider }) {
const { error } = await resend.emails.send({
from: provider.from as string,
to: [email],
subject: "Your verification code", // Change subject
html: `...`, // Customize HTML template
text: `...`, // Customize plain text version
});
}Testing Email Flows
1. Sign Up with Email Verification
// In your sign-up component
await signIn("password", {
flow: "signUp",
email: "user@example.com",
password: "password123"
});
// User receives verification code email2. Password Reset
// Initiate password reset
await signIn("password", {
flow: "reset",
email: "user@example.com"
});
// User receives reset code email
// Submit reset code and new password
await signIn("password", {
flow: "reset-verification",
email: "user@example.com",
code: "123456",
newPassword: "newPassword123"
});Troubleshooting
Common Issues
-
No emails being sent
- Check your Resend API key is correct
- Verify your sending domain in Resend
- Check Convex logs for errors
-
"Failed to send verification email" error
- Ensure RESEND_API_KEY is set in environment variables
- Check that the from email is verified in Resend
-
Email links not working
- Verify SITE_URL is set correctly in environment variables
- Ensure it matches your actual domain
Testing in Development
For local development, you can:
- Use Resend's test API key (starts with
re_test_) - Set SITE_URL to
http://localhost:3003 - Use a verified email domain or Resend's test domain
Advanced Configuration
Rate Limiting
Resend automatically handles rate limiting, but you can implement additional controls:
// Add rate limiting to email sending
const lastEmailSent = await ctx.db
.query("emailLogs")
.withIndex("by_email", (q) => q.eq("email", email))
.order("desc")
.first();
if (lastEmailSent && Date.now() - lastEmailSent.sentAt < 60000) {
throw new Error("Please wait before requesting another email");
}Email Analytics
Track email events using Resend webhooks:
- Set up webhook endpoint in your API
- Configure webhook URL in Resend dashboard
- Handle events (delivered, opened, clicked, etc.)
Security Considerations
- Verification Code Expiry: Codes expire after 15 minutes
- One-Time Use: Each code can only be used once
- Rate Limiting: Implement rate limiting to prevent abuse
- Secure Random Codes: Using 6-digit numeric codes generated securely
Production Checklist
- Set up production Resend API key
- Verify production sending domain
- Update AUTH_RESEND_FROM with production email
- Set correct SITE_URL for production
- Test all email flows in production
- Set up email monitoring/alerts
- Configure SPF/DKIM records for better deliverability