Skip to main content

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

MethodDescriptionAuth RequiredHTTP Endpoint
setupGenerate 2FA QR code for setupPOST /2fa/setup
enableEnable 2FA after scanning QR codePOST /2fa/enable
disableDisable 2FA with token and passwordPOST /2fa/disable
getStatusCheck current 2FA statusGET /2fa/status
getBackupCodesGenerate backup recovery codesPOST /2fa/backup-codes
verifyLoginVerify 2FA token during loginPOST /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-key required

enable

Enable two-factor authentication after user scans QR code and enters verification token.

Syntax

const result = await twofa.enable(token);

Parameters

ParameterTypeRequiredDescription
tokenstring6-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-key required

disable

Disable two-factor authentication with current token and password verification.

Syntax

const result = await twofa.disable(token, password);

Parameters

ParameterTypeRequiredDescription
tokenstringCurrent 6-digit token from authenticator app
passwordstringUser'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-key required

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-key required

getBackupCodes

Generate new backup recovery codes for account recovery when authenticator is unavailable.

Syntax

const result = await twofa.getBackupCodes(password);

Parameters

ParameterTypeRequiredDescription
passwordstringUser'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-key required

verifyLogin

Verify 2FA token during the login process (used with auth.completeLoginWith2FA).

Syntax

const result = await twofa.verifyLogin(userId, token);

Parameters

ParameterTypeRequiredDescription
userIdnumberUser ID from initial login
tokenstring6-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-key required (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

  1. QR Code Security: Display QR codes only over HTTPS and clear them after setup
  2. Backup Codes: Store offline, each code is single-use only
  3. Token Validation: Always validate tokens server-side
  4. Rate Limiting: Implement rate limiting for 2FA attempts
  5. Recovery Flow: Provide clear backup code usage instructions
  6. User Education: Explain authenticator app requirements clearly

Error Handling

ErrorDescriptionSolution
400: Invalid tokenWrong 6-digit codeCheck authenticator app time sync
401: Authentication requiredNo JWT tokenLogin first
403: 2FA already enabledUser already has 2FACheck status first
403: Invalid passwordWrong password for disable/backupVerify current password
429: Too many attemptsRate limitedWait before retry
500: Setup failedServer error during setupRetry 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