OTP Plugin
Email-based one-time password authentication
The OTP plugin provides email-based one-time password authentication for passwordless sign-in, email verification, and password reset flows.
Overview
The OTP (One-Time Password) plugin enables authentication via 6-digit codes sent to the user's email. This provides:
- Passwordless sign-in option
- Email verification without clicking links
- Secure password reset flow
Configuration
The plugin is enabled by default and requires no additional environment variables. It uses the configured mailer to send OTP codes.
Email Configuration
The OTP plugin uses your existing email configuration:
MAILER_PROVIDER=nodemailerEMAIL_SENDER=noreply@yourapp.comEMAIL_HOST=smtp.example.comEMAIL_PORT=587EMAIL_USER=your_userEMAIL_PASSWORD=your_passwordHow It Works
OTP Types
The plugin supports three OTP types:
| Type | Purpose | Email Template |
|---|---|---|
sign-in | Passwordless authentication | otp-sign-in.email.tsx |
email-verification | Verify email address | otp-email-verification.email.tsx |
forget-password | Password reset | otp-password-reset.email.tsx |
OTP Lifecycle
- User requests an OTP (via sign-in, verification, or password reset)
- A 6-digit code is generated and sent via email
- User enters the code in the application
- Code is validated and action is completed
Development Mode
In development (NODE_ENV=development), OTP codes are logged to the console for testing:
[DEV] OTP for user@example.com (sign-in): 847293This allows testing without checking email in development.
Implementation Details
Location
packages/better-auth/src/plugins/otp-auth.tsEmail Templates
OTP emails use dedicated templates in packages/email-templates/src/emails/:
otp-sign-in.email.tsx- Sign-in OTPotp-email-verification.email.tsx- Email verification OTPotp-password-reset.email.tsx- Password reset OTPotp.email.tsx- Generic OTP template with i18n support
Integration
The plugin integrates with Better Auth's emailOTP plugin:
import { emailOTP } from 'better-auth/plugins/email-otp';const otpPlugin = emailOTP({ sendVerificationOTP: async ({ email, otp, type }) => { // Routes to appropriate email template based on type switch (type) { case 'sign-in': await sendOtpSignInEmail({ email, otp }); break; case 'email-verification': await sendOtpEmailVerificationEmail({ email, otp }); break; case 'forget-password': await sendOtpPasswordResetEmail({ email, otp }); break; } },});Usage Example
Client-Side Sign-In with OTP
import { authClient } from '@kit/better-auth/client';// Request OTPawait authClient.emailOtp.sendVerificationOtp({ email: 'user@example.com', type: 'sign-in',});// Verify OTPconst result = await authClient.emailOtp.verifyOtp({ email: 'user@example.com', otp: '123456',});Next: Rate Limiting