BetterStarter logoBetterStarter
Guides

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

  1. Create a free account at useplunk.com
  2. Verify your sending domain in the Plunk dashboard
  3. 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.com

3. Test It

  1. Start the dev server: pnpm dev
  2. Navigate to http://localhost:3000/auth/sign-in
  3. Enter an email address and request a code
  4. Check your server console for the OTP (it's logged during development)
  5. 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_SECONDS in src/constants/auth.ts if needed.
  • Email going to spam: Make sure your sending domain has proper SPF, DKIM, and DMARC records configured.

On this page