⚡ You're viewing a live demo of ChimerAI. Data resets daily at midnight UTC.Get the CLI →

Multi-Factor Authentication (MFA / 2FA) Guide

This guide explains the TOTP-based two-factor authentication feature that ships with ChimerAI and how to install it via the CLI.


Installation

chimerai add mfa

This command installs the following files:

FilePurpose
app/(app)/settings/mfa/page.tsxMFA setup and management UI
lib/mfa.tsTOTP helper functions
app/api/mfa/setup/route.tsGenerate a new TOTP secret and QR code
app/api/mfa/verify/route.tsConfirm the setup by validating the first code
app/api/mfa/disable/route.tsDisable MFA (requires valid code)

Dependencies installed automatically:

otpauth ^9.0.0
qrcode ^1.5.3
@types/qrcode ^1.5.5

Prisma schema changes:

Two fields are added to the User model and a new MfaBackupCode model is created:

// Added to User model:
mfaSecret       String?
mfaEnabled      Boolean @default(false)
mfaBackupCodes  MfaBackupCode[]

model MfaBackupCode {
  id        String    @id @default(cuid())
  userId    String
  codeHash  String
  usedAt    DateTime?
  createdAt DateTime  @default(now())
  user      User      @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@index([userId])
}

After installation, run:

npx prisma migrate dev --name add-mfa

How It Works

ChimerAI uses TOTP (Time-based One-Time Password) as defined in RFC 6238. The user scans a QR code with any authenticator app and then enters 6-digit codes that rotate every 30 seconds.

Supported authenticator apps:

  • Google Authenticator
  • Authy
  • Microsoft Authenticator
  • 1Password
  • Any TOTP-compatible app

lib/mfa.ts

generateMfaSetup(userEmail, appName?)

Generates a new TOTP secret and returns a QR code data URL ready to be displayed as an <img>. The secret is stored on the user record as mfaSecret with mfaEnabled = false until the user confirms with a valid code.

import { generateMfaSetup } from '@/lib/mfa';

const { secret, qrCodeDataUrl } = await generateMfaSetup('user@example.com', 'MyApp');

Returns:

{
  secret: string; // Base32 TOTP secret (store in DB)
  otpauthUrl: string; // otpauth:// URI
  qrCodeDataUrl: string; // data:image/png;base64,...
}

verifyMfaCode(secret, code)

Validates a 6-digit TOTP code against a stored secret. Accepts a drift window of +-1 period (30 seconds) to handle clock skew.

import { verifyMfaCode } from '@/lib/mfa';

const valid = verifyMfaCode(user.mfaSecret, '123456');
if (!valid) return NextResponse.json({ error: 'Invalid code' }, { status: 400 });

API Routes

POST /api/mfa/setup

Generates a new TOTP secret for the authenticated user, stores it in the DB as pending (mfaEnabled = false), and returns the QR code.

Response:

{
  "qrCodeDataUrl": "data:image/png;base64,...",
  "secret": "JBSWY3DPEHPK3PXP"
}

POST /api/mfa/verify

Confirms MFA setup by validating the first code entered by the user. On success, sets mfaEnabled = true.

Request body:

{ "code": "123456" }

Response:

{ "success": true }

POST /api/mfa/disable

Disables MFA for the authenticated user. Requires a valid current TOTP code as confirmation.

Request body:

{ "code": "123456" }

Response:

{ "success": true }

On success, clears mfaSecret and sets mfaEnabled = false.


MFA Page

The page at /settings/mfa guides the user through the full setup flow:

  1. idle - shows a "Set up 2FA" button
  2. setup - shows the QR code, the manual key, and a code input field
  3. done - confirms that 2FA is enabled with an option to disable
  4. disable - asks for a valid code before disabling

The UI handles loading and error states automatically.


Enforcing MFA at Login

After installation, you need to integrate MFA into your NextAuth authorize callback or a middleware check. Example in lib/auth.ts:

// In your credentials provider authorize():
const user = await prisma.user.findUnique({ where: { email } });

if (user.mfaEnabled) {
  // Require MFA code as an additional credentials field
  const mfaCode = credentials.mfaCode;
  if (!mfaCode || !verifyMfaCode(user.mfaSecret, mfaCode)) {
    throw new Error('Invalid MFA code');
  }
}

Notes

  • The TOTP algorithm uses SHA1, 6 digits, 30-second periods - compatible with all major authenticator apps.
  • The secret is stored in plain text in the database. Consider encrypting it at rest for high-security applications.
  • Backup codes (MfaBackupCode model) are generated as hashed values. The CLI creates the model but does not generate backup codes automatically - this is left as a customization point.
  • The verifyMfaCode function is synchronous and has no network calls - it is safe to call in middleware.
ChimerAI Docs · Back to Demo