Email OTP (Passwordless)
Email OTP provides passwordless authentication using one-time codes sent via email. Users sign in by entering a 6-digit code instead of a password.
Configuration
// edgebase.config.ts
export default defineConfig({
auth: {
emailOtp: {
enabled: true,
autoCreate: true, // Auto-create user on first OTP request (default: true)
},
},
});
How It Works
- User requests OTP with their email address
- Server generates a 6-digit code and sends it via the configured email provider
- Code is stored in KV with a 5-minute TTL
- User submits the code for verification
- On success, a session is created (same as email/password sign-in)
New Users
When autoCreate is true (default), requesting an OTP for an unregistered email automatically creates a new user account. The account is created with verified: true and no password.
Rate Limits
- OTP requests: 5 per email per hour
- Verification attempts: 5 per code (after 5 failed attempts, the code is invalidated)
Client Usage
// Request OTP
await client.auth.signInWithEmailOtp({ email: 'user@example.com' });
// Verify OTP
const { user, accessToken } = await client.auth.verifyEmailOtp({
email: 'user@example.com',
code: '123456',
});
REST API
Request OTP
POST /api/auth/signin/email-otp
Content-Type: application/json
{ "email": "user@example.com" }
Response: { "ok": true }
Verify OTP
POST /api/auth/verify-email-otp
Content-Type: application/json
{ "email": "user@example.com", "code": "123456" }
Response: { "user": {...}, "accessToken": "...", "refreshToken": "..." }
MFA Support
If the user has TOTP-based MFA enabled, verifying the OTP code returns an MFA challenge instead of a session:
{ "mfaRequired": true, "mfaTicket": "...", "factors": [...] }
Complete the MFA flow as documented in the MFA guide.
Dev Mode
When no email provider is configured, the OTP code is logged to the server console and included in the response for development convenience:
{ "ok": true, "code": "123456" }
Disabled Accounts
If the user's account has been disabled, the verification step returns 403 Forbidden.