kavachOS
AuthenticationAuth methods

Email OTP

Six-digit one-time codes sent via email for passwordless sign-in.

Email OTP sends a short numeric code to the user's inbox. It works well for mobile flows where clicking a link is awkward and for second-factor verification.

Setup

Add the plugin

lib/kavach.ts
import { createKavach } from '@kavachos/core';
import { emailOtp } from '@kavachos/core/plugins/email-otp';

const kavach = await createKavach({
  database: { provider: 'postgres', url: process.env.DATABASE_URL! },
  secret: process.env.KAVACH_SECRET!,
  baseUrl: 'https://auth.example.com',
  plugins: [
    emailOtp({
      sendEmail: async ({ to, code }) => {
        await resend.emails.send({
          from: 'auth@example.com',
          to,
          subject: `Your code: ${code}`,
          html: `<p>Your sign-in code is <strong>${code}</strong>. It expires in 10 minutes.</p>`,
        });
      },
    }),
  ],
});

The sign-in flow

  1. User submits their email to POST /auth/email-otp/send.
  2. KavachOS generates a six-digit code (cryptographically random) and calls your sendEmail function.
  3. User enters the code in your UI and submits to POST /auth/email-otp/verify.
  4. On success, a session cookie is set.

Sending a code

POST /auth/email-otp/send

await fetch('/auth/email-otp/send', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email: 'user@example.com' }),
});

Verifying the code

POST /auth/email-otp/verify

const res = await fetch('/auth/email-otp/verify', {
  method: 'POST',
  credentials: 'include',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email: 'user@example.com', code: '482910' }),
});

const { userId, sessionId } = await res.json();

Options

OptionTypeDefaultDescription
sendEmailfunctionrequiredCalled with { to, code }
codeLengthnumber6Length of the OTP code
codeTtlnumber600Code lifetime in seconds (default: 10 minutes)
maxAttemptsnumber5Failed attempts before the code is invalidated
createUserIfNotFoundbooleantrueAuto-create accounts for new emails

Codes are rate-limited to one per minute per email address. Requests within the window return 429. Build a countdown timer into your UI.

On this page