Multi-Factor Authentication
Multi-factor authentication (MFA) adds a second verification step after your password, significantly reducing the risk of unauthorized account access. Even if a password is compromised, an attacker cannot sign in without the second factor.
UniAuth supports three MFA methods: TOTP (authenticator apps), email OTP, and SMS OTP. Users can enable multiple methods simultaneously and choose a preferred default.
Supported Methods
| Method | How It Works | Requirements |
|---|---|---|
| TOTP | Time-based codes generated by an authenticator app | Authenticator app (Google Authenticator, Authy, 1Password, etc.) |
| Email OTP | 6-digit code sent to your email address | Verified email address |
| SMS OTP | 6-digit code sent via text message | Verified phone number + Twilio configured by admin |
Recommendation: Use TOTP as your primary MFA method. It works offline, does not depend on email or SMS delivery, and is the most resistant to interception. Keep backup codes stored securely as a fallback.
Enabling MFA
Users enable MFA from their Account → Security page. The setup process varies by method but follows a consistent pattern: initiate setup, complete verification, and confirm activation.
TOTP (Authenticator Apps)
TOTP (Time-based One-Time Password) generates a new 6-digit code every 30 seconds using a shared secret between UniAuth and your authenticator app. This is the most secure and reliable MFA method because codes are generated locally on your device.
Compatible Apps
- Google Authenticator (iOS, Android)
- Authy (iOS, Android, Desktop)
- 1Password (iOS, Android, Mac, Windows)
- Microsoft Authenticator (iOS, Android)
- Any app that supports RFC 6238 TOTP
Setup Flow
- Navigate to Account → Security and click Enable TOTP.
- UniAuth generates a unique secret and displays a QR code. Open your authenticator app and scan the QR code. You can also enter the secret manually if scanning is not available.
- Enter the 6-digit code currently shown in your authenticator app to verify the setup.
- UniAuth confirms activation and provides a set of backup codes. Store these in a safe place — they are your recovery option if you lose access to your authenticator app.
API: Setup TOTP
POST /api/auth/2fa/setup
Content-Type: application/json
Cookie: auth_token=...
{
"method": "totp"
}
// Response (200):
{
"success": true,
"qrCode": "otpauth://totp/UniAuth:[email protected]?secret=BASE32SECRET&issuer=UniAuth",
"secret": "BASE32SECRET",
"backupCodes": ["abc12345", "def67890", "ghi13579", ...]
}API: Verify TOTP Setup
POST /api/auth/2fa/verify
Content-Type: application/json
Cookie: auth_token=...
{
"code": "123456",
"method": "totp"
}
// Response (200):
{
"success": true,
"message": "TOTP two-factor authentication enabled"
}Security: The TOTP secret is encrypted with AES-256-GCM before being stored in the database. It is never exposed again after the initial setup step.
Email OTP
Email OTP sends a 6-digit verification code to your registered email address each time you need to verify your identity. The code is valid for 10 minutes and can only be used once.
How It Works
- During login, after entering your password, UniAuth sends a code to your email.
- Check your inbox for an email from UniAuth containing the 6-digit code.
- Enter the code on the verification screen to complete login.
Enabling Email OTP
Your email address must be verified before you can enable email OTP. Navigate to Account → Security and select Enable Email OTP. A verification code is sent to confirm the setup.
API: Send Email Code
POST /api/auth/2fa/send-code
Content-Type: application/json
{
"userId": "user-uuid",
"method": "email"
}
// Response (200):
{
"success": true,
"message": "Verification code sent to your email"
}Note: The OTP code is encrypted with AES-256-GCM before storage. Expired codes are automatically purged every 24 hours.
SMS OTP
SMS OTP sends a 6-digit verification code via text message to your verified phone number. Like email OTP, the code is valid for 10 minutes and is single-use.
Prerequisites
- Your phone number must be verified in your security settings.
- The UniAuth instance must have Twilio configured. See the Configuration guide for Twilio setup.
Enabling SMS OTP
- Go to Account → Security.
- Add and verify your phone number if you haven't already.
- Click Enable SMS OTP.
- Enter the verification code sent to your phone to confirm setup.
API: Send SMS Code
POST /api/auth/2fa/send-code
Content-Type: application/json
{
"userId": "user-uuid",
"method": "sms"
}
// Response (200):
{
"success": true,
"message": "Verification code sent via SMS"
}Note: SMS OTP is subject to carrier delivery delays and SIM-swapping attacks. For maximum security, prefer TOTP over SMS. SMS 2FA is only available when Twilio is configured by the UniAuth administrator.
Default Method
When you have multiple MFA methods enabled, UniAuth uses your default method as the first option during login. You can always switch to a different enabled method on the verification screen.
Set your default method in Account → Security, or via the API:
POST /api/auth/2fa/set-default
Content-Type: application/json
Cookie: auth_token=...
{
"method": "totp" // "totp", "email", or "sms"
}
// Response (200):
{
"success": true,
"message": "Default 2FA method updated to totp"
}Using Multiple Methods
UniAuth allows you to enable all three MFA methods at the same time. This gives you flexibility and redundancy:
- Use TOTP as your everyday method (fast, offline, secure).
- Keep email OTP as a backup in case you lose your authenticator device.
- Add SMS OTP as a last-resort fallback.
During login, the verification screen shows your default method first with an option to switch. For example, if TOTP is your default and you don't have your phone, click "Use a different method" to receive a code via email instead.
Verification During Login
When MFA is enabled, the login process has two steps:
- Password verification — Submit email and password as usual. The API responds with a 2FA challenge instead of completing login.
- MFA verification — Submit the 6-digit code from your chosen method to complete authentication.
Step 1: Login Returns a 2FA Challenge
POST /api/auth/login
Content-Type: application/json
{
"email": "[email protected]",
"password": "your-password"
}
// Response when 2FA is enabled (200):
{
"success": true,
"requires2FA": true,
"userId": "550e8400-e29b-41d4-a716-446655440000",
"defaultMethod": "totp",
"availableMethods": ["totp", "email", "sms"]
}The response includes the user's default method and all available methods so your application can display the appropriate verification UI.
Step 2: Submit the Verification Code
For email and SMS methods, first trigger code delivery, then submit the code:
// If using email or SMS, request a code first:
POST /api/auth/2fa/send-code
Content-Type: application/json
{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"method": "email"
}
// Then submit the verification code:
POST /api/auth/2fa/verify
Content-Type: application/json
{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"code": "123456",
"method": "email"
}
// Success response (200):
{
"success": true,
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"firstName": "Jane",
"lastName": "Doe"
}
}On successful verification, UniAuth creates a session and sets the authentication cookie just as it would for a regular login.
For Developers: Integrating MFA
If you're building an application that uses UniAuth for authentication, here's how to handle the MFA flow in your login implementation.
Detecting a 2FA Challenge
After submitting credentials, check the response for the requires2FA flag:
async function login(email, password) {
const res = await fetch('https://uniauth.id/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
const data = await res.json();
if (data.requires2FA) {
// Show the 2FA verification screen
show2FAScreen({
userId: data.userId,
defaultMethod: data.defaultMethod,
availableMethods: data.availableMethods,
});
return;
}
if (data.success) {
// Login complete — no 2FA required
redirectToDashboard(data.user);
}
}Completing 2FA Verification
async function verify2FA(userId, code, method) {
// For email/SMS methods, request a code first
if (method === 'email' || method === 'sms') {
await fetch('https://uniauth.id/api/auth/2fa/send-code', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId, method }),
});
// Wait for user to enter the code...
}
// Submit the verification code
const res = await fetch('https://uniauth.id/api/auth/2fa/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId, code, method }),
});
const data = await res.json();
if (data.success) {
redirectToDashboard(data.user);
} else {
showError('Invalid verification code. Please try again.');
}
}Error Handling
| Status | Meaning | Action |
|---|---|---|
200 | Verification successful | Session created, proceed to app |
400 | Invalid or expired code | Prompt user to re-enter code or request a new one |
423 | Account locked (too many failed attempts) | Show lockout message with remaining time |
429 | Rate limited | Wait before retrying (10 attempts per 15 minutes) |
Backup Codes
Backup codes are single-use recovery codes generated when you first enable MFA. They allow you to bypass 2FA verification if you lose access to your authenticator app, email, or phone.
- Each backup code can only be used once.
- Backup codes are hashed with SHA-256 before storage — UniAuth cannot recover them for you.
- Store them in a password manager, printed on paper in a safe location, or in an encrypted note.
- You can regenerate a new set of backup codes from your security settings (this invalidates all previous codes).
Important: Treat backup codes like passwords. Anyone who has a backup code can bypass your 2FA protection. If you suspect your backup codes have been compromised, regenerate them immediately.
Best Practices
- Use TOTP as your primary method. It is the fastest and most secure option — codes are generated offline and cannot be intercepted in transit.
- Enable a backup method. If TOTP is your primary, enable email OTP as a fallback so you can still log in if you lose your authenticator device.
- Store backup codes securely. Save them in your password manager or print them and keep them in a secure physical location.
- Avoid SMS as your only method. SMS is vulnerable to SIM-swapping and carrier-level interception. Use it as a last-resort fallback, not as your primary factor.
- Consider passkeys. For the highest security, combine MFA with passkey authentication, which eliminates the password entirely.
Disabling MFA
You can disable individual MFA methods or all of them from Account → Security. Disabling requires re-authentication with your current password for security.
When you disable your last remaining MFA method, your account returns to password-only authentication. Your backup codes are invalidated when all MFA methods are disabled.
Tip: If you're locked out and cannot access any of your MFA methods or backup codes, contact your UniAuth administrator who can temporarily disable MFA on your account from the Admin Panel.