Auth
Setup email-based authentication with Magic Links and OTP.
How It Works
BetterStarter uses Better Auth with the emailOTP plugin. When a user signs in, a 6-digit code is sent to their email. They enter the code to complete authentication. No passwords required.
1. Set Up an Email Provider
OTP codes are delivered via Plunk, a transactional email service. You can swap this for any provider (Resend, SendGrid, Postmark, etc.) by editing the sendOTPEmail function.
Plunk Setup
- Create a free account at useplunk.com
- Verify your sending domain in the Plunk dashboard
- Copy your secret API key
2. Configure Environment Variables
Add the following to your .env.local:
# Plunk API key
PLUNK_SECRET_API_KEY=your_plunk_secret_api_key
# Verified sending email address
TRANSACTIONAL_EMAIL=noreply@yourdomain.com3. Test It
- Start the dev server:
pnpm dev - Navigate to
http://localhost:3000/auth/sign-in - Enter an email address and request a code
- Check your server console for the OTP (it's logged during development)
- Enter the 6-digit code to complete sign-in
Configuration
OTP settings are defined in src/constants/auth.ts:
export const OTP_CONFIG = {
LENGTH: 6, // Number of digits
EXPIRES_IN_SECONDS: 300, // 5 minutes
}The email sending logic lives in the Better Auth config at src/lib/auth/index.ts. The emailOTP plugin calls sendOTPEmail whenever a verification code is requested.
Swapping Email Providers
To use a different email service, update the sendOTPEmail function to call your preferred provider's API. The function receives the recipient email, the OTP code, and the verification type.
Troubleshooting
- No email received: Check the server console. OTP codes are logged during development. In production, verify your Plunk API key and that your sending domain is verified.
- Code expired: The default expiry is 5 minutes. Adjust
EXPIRES_IN_SECONDSinsrc/constants/auth.tsif needed. - Email going to spam: Make sure your sending domain has proper SPF, DKIM, and DMARC records configured.