Two-Factor Authentication Module
The twofa module provides secure two-factor authentication setup and management for enhanced account security.
Import
const twofa = await brdzSDK.twofa;
Methods Overview
| Method | Description | Auth Required | HTTP Endpoint |
|---|---|---|---|
setup | Generate 2FA QR code for setup | ✅ | POST /2fa/setup |
enable | Enable 2FA after scanning QR code | ✅ | POST /2fa/enable |
disable | Disable 2FA with token and password | ✅ | POST /2fa/disable |
getStatus | Check current 2FA status | ✅ | GET /2fa/status |
getBackupCodes | Generate backup recovery codes | ✅ | POST /2fa/backup-codes |
verifyLogin | Verify 2FA token during login | ❌ | POST /2fa/verify-login |
setup
Generate QR code and secret key for 2FA setup with authenticator apps.
Syntax
const result = await twofa.setup();
Parameters
None - uses authenticated user's session.
Returns
Promise<{
success: boolean;
data: {
secret: string; // Secret key for manual entry
qr_code: string; // QR code image data URI
backup_codes: string[]; // Initial backup codes (8 codes)
manual_entry_key: string; // Same as secret
issuer: string; // "BRDZ SDK Dashboard"
account_name: string; // User's email
};
}>
Example
try {
const setupResult = await twofa.setup();
console.log('QR Code:', setupResult.data.qr_code);
console.log('Manual secret:', setupResult.data.secret);
console.log('Backup codes:', setupResult.data.backup_codes);
// Display QR code in UI
document.getElementById('qr-code').src = setupResult.data.qr_code;
// Show manual entry option
document.getElementById('manual-secret').textContent = setupResult.data.manual_entry_key;
// Store backup codes securely
console.warn('Save these backup codes safely:', setupResult.data.backup_codes);
} catch (error) {
console.error('2FA setup failed:', error.message);
}
Integration with Authenticator Apps
Popular authenticator apps that work with BRDZ 2FA:
- Google Authenticator (iOS/Android)
- Authy (iOS/Android/Desktop)
- Microsoft Authenticator (iOS/Android)
- 1Password (Cross-platform)
- Bitwarden (Cross-platform)
HTTP Endpoint
- Method:
POST - URL:
https://api.brdz.link/api/2fa/setup - Headers:
Authorization: Bearer <JWT_TOKEN>,x-api-keyrequired
enable
Enable two-factor authentication after user scans QR code and enters verification token.
Syntax
const result = await twofa.enable(token);
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
token | string | ✅ | 6-digit token from authenticator app |
Returns
Promise<{
success: boolean;
message: string;
}>
Example
// After user scans QR code and enters token from authenticator app
const userToken = '123456'; // From authenticator app
try {
const enableResult = await twofa.enable(userToken);
if (enableResult.success) {
console.log('2FA successfully enabled!');
console.log('Message:', enableResult.message);
// Show success message
alert('2FA enabled successfully!');
// Update UI to show 2FA is enabled
updateSecuritySettings({ twofa_enabled: true });
}
} catch (error) {
console.error('2FA enable failed:', error.message);
// Usually means invalid token from authenticator app
}
HTTP Endpoint
- Method:
POST - URL:
https://api.brdz.link/api/2fa/enable - Headers:
Authorization: Bearer <JWT_TOKEN>,x-api-keyrequired
disable
Disable two-factor authentication with current token and password verification.
Syntax
const result = await twofa.disable(token, password);
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
token | string | ✅ | Current 6-digit token from authenticator app |
password | string | ✅ | User's current password for verification |
Returns
Promise<{
success: boolean;
message: string;
}>
Example
// User wants to disable 2FA
const currentToken = '654321'; // From authenticator app
const currentPassword = 'userpassword123';
try {
const disableResult = await twofa.disable(currentToken, currentPassword);
if (disableResult.success) {
console.log('2FA has been disabled');
console.log('Message:', disableResult.message);
alert('Two-factor authentication has been disabled for your account.');
// Update UI to show 2FA is off
updateSecuritySettings({ twofa_enabled: false });
}
} catch (error) {
console.error('2FA disable failed:', error.message);
// Could be invalid token, wrong password, or other issues
}
HTTP Endpoint
- Method:
POST - URL:
https://api.brdz.link/api/2fa/disable - Headers:
Authorization: Bearer <JWT_TOKEN>,x-api-keyrequired
getStatus
Check the current two-factor authentication status for the authenticated user.
Syntax
const result = await twofa.getStatus();
Parameters
None - uses authenticated user's session.
Returns
Promise<{
success: boolean;
data: {
is_enabled: boolean; // Whether 2FA is currently enabled
is_setup: boolean; // Whether initial setup was completed
setup_completed_at: string | null; // ISO timestamp or null
last_used_at: string | null; // ISO timestamp or null
backup_codes_count: number; // Number of unused backup codes
};
}>
Example
try {
const status = await twofa.getStatus();
console.log('2FA enabled:', status.data.is_enabled);
console.log('Setup completed:', status.data.is_setup);
console.log('Setup date:', status.data.setup_completed_at);
console.log('Last used:', status.data.last_used_at);
console.log('Backup codes remaining:', status.data.backup_codes_count);
// Update security settings UI
const securitySettings = {
twofa_enabled: status.data.is_enabled,
needs_setup: !status.data.is_setup,
backup_codes_low: status.data.backup_codes_count < 3
};
updateSecurityUI(securitySettings);
if (securitySettings.backup_codes_low) {
console.warn('Low backup codes remaining. Generate new ones.');
}
} catch (error) {
console.error('Failed to get 2FA status:', error.message);
}
HTTP Endpoint
- Method:
GET - URL:
https://api.brdz.link/api/2fa/status - Headers:
Authorization: Bearer <JWT_TOKEN>,x-api-keyrequired
getBackupCodes
Generate new backup recovery codes for account recovery when authenticator is unavailable.
Syntax
const result = await twofa.getBackupCodes(password);
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
password | string | ✅ | User's current password for verification |
Returns
Promise<{
success: boolean;
data: {
backup_codes: string[]; // Array of backup codes (8 codes)
};
}>
Example
// User requests backup codes
const currentPassword = 'userpassword123';
try {
const backupResult = await twofa.getBackupCodes(currentPassword);
console.log('Backup codes:', backupResult.data.backup_codes);
// Display backup codes to user for download/printing
displayBackupCodes(backupResult.data.backup_codes);
// Provide download functionality
const codesText = backupResult.data.backup_codes.join('\n');
const blob = new Blob([codesText], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const downloadLink = document.createElement('a');
downloadLink.href = url;
downloadLink.download = 'brdz-backup-codes.txt';
downloadLink.click();
alert('Backup codes retrieved. Please save them safely!');
} catch (error) {
console.error('Failed to get backup codes:', error.message);
}
Security Best Practices
- Store backup codes offline (printed or secure password manager)
- Each backup code can only be used once
- Generate new codes if you suspect they're compromised
- Don't store backup codes in the same place as passwords
HTTP Endpoint
- Method:
POST - URL:
https://api.brdz.link/api/2fa/backup-codes - Headers:
Authorization: Bearer <JWT_TOKEN>,x-api-keyrequired
verifyLogin
Verify 2FA token during the login process (used with auth.completeLoginWith2FA).
Syntax
const result = await twofa.verifyLogin(userId, token);
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
userId | number | ✅ | User ID from initial login |
token | string | ✅ | 6-digit token or backup code |
Returns
Promise<{
success: boolean;
message: string;
}>
Example
// Called during login flow when 2FA is required
try {
const verification = await twofa.verifyLogin(userId, '123456');
if (verification.success) {
console.log('2FA verification successful');
console.log('Message:', verification.message);
// Proceed with completing the login
// This is usually followed by auth.completeLoginWith2FA()
}
} catch (error) {
console.error('2FA verification failed:', error.message);
}
HTTP Endpoint
- Method:
POST - URL:
https://api.brdz.link/api/2fa/verify-login - Headers:
x-api-keyrequired (no JWT token needed)
Complete 2FA Implementation Example
2FA Setup Flow
class TwoFAManager {
constructor(sdk) {
this.sdk = sdk;
}
async setupTwoFA() {
const twofa = await this.sdk.twofa;
try {
// Step 1: Generate QR code
const setup = await twofa.setup();
// Step 2: Display QR code to user
this.displayQRCode(setup.data.qr_code, setup.data.secret);
// Step 3: Store backup codes securely
this.storeBackupCodes(setup.data.backup_codes);
return {
qr_code: setup.data.qr_code,
secret: setup.data.secret,
backup_codes: setup.data.backup_codes
};
} catch (error) {
throw new Error(`2FA setup failed: ${error.message}`);
}
}
async enableTwoFA(userToken) {
const twofa = await this.sdk.twofa;
try {
const result = await twofa.enable(userToken);
if (result.success) {
// Update user settings
this.updateUserSettings({ twofa_enabled: true });
return { success: true };
}
} catch (error) {
throw new Error(`Failed to enable 2FA: ${error.message}`);
}
}
async disableTwoFA(token, password) {
const twofa = await this.sdk.twofa;
try {
const result = await twofa.disable(token, password);
if (result.success) {
// Update user settings
this.updateUserSettings({ twofa_enabled: false });
// Clear stored backup codes
this.clearBackupCodes();
return { success: true };
}
} catch (error) {
throw new Error(`Failed to disable 2FA: ${error.message}`);
}
}
async checkStatus() {
const twofa = await this.sdk.twofa;
try {
const status = await twofa.getStatus();
return status.data;
} catch (error) {
throw new Error(`Failed to get 2FA status: ${error.message}`);
}
}
async getBackupCodes(password) {
const twofa = await this.sdk.twofa;
try {
const result = await twofa.getBackupCodes(password);
// Store new backup codes
this.storeBackupCodes(result.data.backup_codes);
return result.data.backup_codes;
} catch (error) {
throw new Error(`Failed to get backup codes: ${error.message}`);
}
}
// Helper methods
displayQRCode(qrUrl, secret) {
const qrContainer = document.getElementById('qr-code-container');
qrContainer.innerHTML = `
<div>
<h3>Scan QR Code with Authenticator App</h3>
<img src="${qrUrl}" alt="2FA QR Code" />
<p>Or enter this code manually: <code>${secret}</code></p>
</div>
`;
}
storeBackupCodes(codes) {
// Store securely - consider encryption
localStorage.setItem('brdz_2fa_backup_codes', JSON.stringify(codes));
// Provide download option
this.downloadBackupCodes(codes);
}
downloadBackupCodes(codes) {
const codesText = [
'BRDZ SDK Backup Codes',
'====================',
'Save these codes safely. Each can only be used once.',
'',
...codes.map((code, index) => `${index + 1}. ${code}`)
].join('\n');
const blob = new Blob([codesText], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `brdz-backup-codes-${Date.now()}.txt`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
}
clearBackupCodes() {
localStorage.removeItem('brdz_2fa_backup_codes');
}
updateUserSettings(settings) {
const currentSettings = JSON.parse(localStorage.getItem('user_settings') || '{}');
const newSettings = { ...currentSettings, ...settings };
localStorage.setItem('user_settings', JSON.stringify(newSettings));
}
}
// Usage Example
const twoFAManager = new TwoFAManager(brdzSDK);
// Setup flow
document.getElementById('setup-2fa').addEventListener('click', async () => {
try {
const setup = await twoFAManager.setupTwoFA();
// QR code is displayed, wait for user to scan and enter token
} catch (error) {
alert('Setup failed: ' + error.message);
}
});
// Enable after user enters token
document.getElementById('enable-2fa').addEventListener('click', async () => {
const userToken = document.getElementById('2fa-token').value;
try {
const result = await twoFAManager.enableTwoFA(userToken);
alert('2FA enabled successfully!');
} catch (error) {
alert('Enable failed: ' + error.message);
}
});
Integration with Login Flow
// Complete login with 2FA integration
class AuthWithTwoFA {
async login(email, password) {
const auth = await brdzSDK.auth;
try {
const loginResult = await auth.loginUser(email, password);
if (loginResult.requires_2fa) {
// Show 2FA input form
return {
requires_2fa: true,
user_id: loginResult.user_id,
show_2fa_form: true
};
}
// Regular login successful
this.storeAuthToken(loginResult.token);
return { success: true };
} catch (error) {
throw new Error(`Login failed: ${error.message}`);
}
}
async completeTwoFALogin(userId, token) {
const auth = await brdzSDK.auth;
const twofa = await brdzSDK.twofa;
try {
// First verify the 2FA token
const verification = await twofa.verifyLogin(userId, token);
if (verification.success) {
// Complete the login process
const completeResult = await auth.completeLoginWith2FA(userId, token);
this.storeAuthToken(completeResult.token);
return { success: true };
}
} catch (error) {
throw new Error(`2FA login failed: ${error.message}`);
}
}
storeAuthToken(token) {
localStorage.setItem('auth_token', token);
// Update SDK configuration
brdzSDK.config.setToken(token);
}
}
Database Schema
Based on the backend implementation, the following tables are used:
user_2fa_settings Table
- user_id (reference to users table)
- totp_secret (encrypted TOTP secret)
- backup_codes (array of 8 backup codes)
- is_enabled (boolean)
- setup_completed_at (timestamp)
- last_used_at (timestamp)
- created_at, updated_at (timestamps)
user_2fa_logs Table (Audit Trail)
- user_id (reference to users table)
- action (setup, enabled, disabled, verified, failed_verification, etc.)
- ip_address (client IP)
- user_agent (client browser/app)
- success (boolean)
- failure_reason (text, nullable)
- created_at (timestamp)
Security Features
- Password Verification: Required for disable and backup codes access
- Audit Logging: All 2FA activities logged to user_2fa_logs table
- IP Tracking: Client IP and User-Agent logged for security monitoring
- Token Window: 2-step time tolerance for TOTP verification
- Backup Codes: 8 backup codes generated (8 chars each, hex uppercase)
- Database Persistence: Settings kept for audit even when disabled
Security Best Practices
- QR Code Security: Display QR codes only over HTTPS and clear them after setup
- Backup Codes: Store offline, each code is single-use only
- Token Validation: Always validate tokens server-side
- Rate Limiting: Implement rate limiting for 2FA attempts
- Recovery Flow: Provide clear backup code usage instructions
- User Education: Explain authenticator app requirements clearly
Error Handling
| Error | Description | Solution |
|---|---|---|
400: Invalid token | Wrong 6-digit code | Check authenticator app time sync |
401: Authentication required | No JWT token | Login first |
403: 2FA already enabled | User already has 2FA | Check status first |
403: Invalid password | Wrong password for disable/backup | Verify current password |
429: Too many attempts | Rate limited | Wait before retry |
500: Setup failed | Server error during setup | Retry setup process |
Always implement proper error handling and user feedback for all 2FA operations.
Supported Authenticator Apps
Based on TOTP standard (RFC 6238):
- Google Authenticator
- Authy
- 1Password
- Microsoft Authenticator
- Any TOTP-compatible app
Implementation Notes
- Uses speakeasy library for TOTP generation/verification
- QR codes generated using qrcode library
- Password hashing verified with bcryptjs
- Database transactions ensure data consistency