Quickback Docs

Combo Auth Plugin

@kardoe/better-auth-combo-auth — Combined magic link and email OTP authentication flow.

@kardoe/better-auth-combo-auth provides a combined authentication flow that sends both a magic link and an OTP code in a single email. Users can click the link (one-click) or enter the 6-digit OTP code — whichever is more convenient.

Installation

npm install @kardoe/better-auth-combo-auth

Server Setup

Add the plugin to your Better Auth configuration:

import { betterAuth } from "better-auth";
import { comboAuth } from "@kardoe/better-auth-combo-auth";

export const auth = betterAuth({
  plugins: [
    comboAuth({
      sendComboAuth: async ({ email, otp, url }) => {
        // Send email with both the magic link (url) and OTP code (otp)
        await sendEmail({
          to: email,
          subject: "Sign in to My App",
          html: `
            <p>Click the link below to sign in:</p>
            <a href="${url}">Sign in to My App</a>
            <p>Or enter this code: <strong>${otp}</strong></p>
          `,
        });
      },
    }),
  ],
});

Client Usage

import { createAuthClient } from "better-auth/client";
import { comboAuthClient } from "@kardoe/better-auth-combo-auth/client";

const authClient = createAuthClient({
  plugins: [comboAuthClient()],
});

// Request authentication — sends email with link + OTP
await authClient.signIn.comboAuth({
  email: "user@example.com",
});

// Verify with OTP code (if user enters it manually)
await authClient.signIn.comboAuth({
  email: "user@example.com",
  otp: "123456",
});

How It Works

  1. User enters their email and requests sign-in
  2. A single email is sent containing both a magic link and a 6-digit OTP
  3. Option A: User clicks the magic link — authenticated immediately
  4. Option B: User enters the OTP code — authenticated after verification

This provides the best of both worlds: convenience of magic links with the reliability of OTP codes (useful when magic links don't work well, e.g., opening email on a different device).

See Also

On this page