Skip to main content

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

  1. User requests OTP with their email address
  2. Server generates a 6-digit code and sends it via the configured email provider
  3. Code is stored in KV with a 5-minute TTL
  4. User submits the code for verification
  5. 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.