Skip to main content
Multi-factor Authentication (MFA) adds an essential layer of security to user accounts by requiring users to provide two or more verification factors to gain access. This significantly reduces the risk of unauthorized access, even if a password is compromised. Nile supports two primary MFA methods:
  1. Authenticator Apps: Uses Time-based One-Time Password (TOTP) algorithms, where a code is generated by an authenticator app (e.g., Google Authenticator, Authy) on the user’s device.
  2. Email One-Time Code (OTP): A temporary verification code is sent to the user’s registered email address.

The MFA Flow

The MFA lifecycle in Nile can be broken down into four key stages:

1. Setup (Enrollment)

During setup, a user registers an MFA method with their account. Note: This step assumes the user is already signed up and currently logged in.
  • Client-Side: The client initiates the setup process.
  • Server-Side: The @niledatabase/server package acts as a proxy, forwarding this request to the core Nile API.
  • API Response: The Nile API responds with method-specific information:
    • For Authenticator: An otpauthUrl (for QR code generation), a secret, and initial recoveryKeys.
    • For Email: A maskedEmail and a token to be used for verification.
  • User Action: The user typically scans a QR code (authenticator) or checks their email for a code.
import { auth } from '@niledatabase/client';

// Start enrollment for an authenticator app
const response = await auth.mfa({
  method: 'authenticator',
  scope: 'setup',
});

// Response contains secret, otpauthUrl (for QR), and recoveryKeys
// { method: 'authenticator', secret: '...', otpauthUrl: '...', ... }

2. Challenge

After an MFA method is set up, a “challenge” is issued whenever a user attempts to sign in (or perform certain sensitive actions). This requires the user to provide their second factor.
  • Sign-in Response: When a user signs in with credentials, if MFA is enabled for their account, the sign-in response will often include a token and method indicating that an MFA challenge is required.
  • User Prompt: The application prompts the user to enter a code.
    • React: The <MultiFactorChallenge /> component is designed to handle this interaction seamlessly.
    • Custom UI: Build a form to capture the code and submit it via the client SDK.

3. Verification

This is the core “verify” step where the user’s provided code is checked against the registered MFA method.
  • Client-Side: The client calls the mfa function with:
    • token: The challenge token received from the sign-in response or setup.
    • code: The 6-digit code or recovery code provided by the user.
    • scope: Set to 'challenge' to indicate verification.
    • method: The specific MFA method being verified ('authenticator' or 'email').
  • Server-Side: Again, the @niledatabase/server proxies this request.
  • API Response:
    • Success: Returns { ok: true, scope: 'challenge' } (and potentially recoveryCodesRemaining if a recovery code was used).
    • Failure: Returns an error, often coerced into a { url: string } object (a ChallengeRedirect) with an error query parameter, which your application can then parse and display.
// Verify the code entered by the user
const verification = await auth.mfa({
  token: 'token_from_setup_or_login',
  code: '123456', // 6-digit code or recovery key
  method: 'authenticator',
  scope: 'challenge',
});

if (verification.ok) {
  // Verification successful
}

4. Removal (Disable)

Users can remove an enrolled MFA method from their account.
  • Client-Side: The client calls the mfa function with remove: true and the method to be disabled.
  • Server-Side: Proxied through @niledatabase/server.
  • API Response: On success, the MFA method is removed. In some cases, the API might issue a challenge before removal (e.g., to confirm the user’s identity).
// Remove the authenticator method
await auth.mfa({
  method: 'authenticator',
  remove: true,
});

User Object and MFA Status

The User object, retrieved via auth.getSession(), now includes a multiFactor property. This property indicates whether MFA is enabled for the user, allowing your application to dynamically adjust UI and workflows based on the user’s MFA status.
const session = await auth.getSession();

if (session.user?.multiFactor) {
  // User has MFA enabled
  console.log('MFA Enabled');
}

Configuration and Redirection

Nile Auth handles various redirections during the MFA flow. Specifically, when the backend requires a redirect (e.g., due to an error or successful completion of a step), it will return a { url: string } object. This is referred to as a ChallengeRedirect. Your application should handle these redirects to guide the user through the appropriate next steps or display relevant messages based on the error query parameter in the URL.