Authenticator App Setup
Overview
Complete guide for setting up Time-based One-Time Password (TOTP) authenticator apps for two-factor authentication.
Supported Authenticator Apps
- Google Authenticator (iOS, Android)
- Microsoft Authenticator (iOS, Android)
- Authy (iOS, Android, Desktop)
- LastPass Authenticator
- 1Password
- Any RFC 6238 compliant authenticator
Setup Process
Step 1: Initiate Login with Authenticator Method
Call the login endpoint with verificationMethodType=3:
POST /api/BPMSelfService/commands/SelfAdminLoginCommand
{
"username": "admin@example.com",
"password": "SecurePassword123!",
"verificationMethodType": 3
}
Step 2: Check Setup Requirement
If authenticator is not set up, the response will include:
{
"status": "success",
"message": "Authenticator setup required",
"data": {
"requiresSetup": true,
"qrCodeUrl": "otpauth://totp/BankLingo:admin@example.com?secret=ABC123XYZ&issuer=BankLingo",
"secretKey": "ABC123XYZ"
}
}
Step 3: Display QR Code
Present the QR code to the user. You can:
- Use a QR code library to generate from
qrCodeUrl - Display
secretKeyfor manual entry - Provide both options for flexibility
Step 4: User Scans QR Code
User opens their authenticator app and:
- Taps "Add account" or "+"
- Selects "Scan QR code"
- Scans the displayed QR code
- Sees "BankLingo - admin@example.com" entry in app
Step 5: Verify Setup
User enters the 6-digit code from their authenticator app:
POST /api/BPMSelfService/commands/SelfAdminLoginCommand
{
"username": "admin@example.com",
"password": "SecurePassword123!",
"verificationMethodType": 3,
"setupVerificationCode": "123456"
}
Step 6: Setup Complete
On successful verification:
{
"status": "success",
"message": "Login successful",
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "...",
"expiresIn": 3600
}
}
The secret key is now saved to the user's account for future logins.
Session Management
Abandoned Setup Handling
If a user starts the authenticator setup but doesn't complete it, the system automatically handles this scenario.
Resuming a Setup Session
When a user returns after abandoning setup (within 24 hours), the system will:
- Return the same QR code and secret key
- Allow completion with already-scanned QR code
- Include
isResumedSession: truein response
{
"status": "success",
"message": "Previous authenticator setup session found. Please use the QR code you scanned earlier, or scan this one again.",
"data": {
"requiresSetup": true,
"isResumedSession": true,
"qrCodeUrl": "otpauth://totp/BankLingo:admin@example.com?secret=ABC123XYZ&issuer=BankLingo",
"secretKey": "ABC123XYZ",
"setupStartedAt": "2025-12-25T10:30:00Z"
}
}
User Experience Benefits:
- ✅ No need to rescan QR code if already scanned
- ✅ Codes from previously scanned QR code will work
- ✅ Seamless continuation of setup process
Session Timeout (24 Hours)
If a user returns after 24 hours, the system will:
- Generate a new secret key and QR code
- Clear the old setup session
- Include
sessionExpired: truein response
{
"status": "success",
"message": "Your previous authenticator setup session expired after 24 hours. Please scan this new QR code.",
"data": {
"requiresSetup": true,
"sessionExpired": true,
"qrCodeUrl": "otpauth://totp/BankLingo:admin@example.com?secret=NEW456DEF&issuer=BankLingo",
"secretKey": "NEW456DEF",
"previousSessionExpiredAt": "2025-12-24T10:30:00Z"
}
}
Security Rationale:
- ✅ Old QR codes expire automatically
- ✅ Prevents indefinite pending setup sessions
- ✅ Encourages users to complete setup promptly
Switching Authentication Methods
Users can switch between authentication methods during login:
Scenario 1: Authenticator → Email/SMS OTP
If a user starts authenticator setup but then switches to email/SMS OTP:
- Abandoned authenticator session is automatically cleared
- Email/SMS OTP works normally
- No manual cleanup required
Scenario 2: Email/SMS → Authenticator
If a user starts with email/SMS OTP but then switches to authenticator:
- System detects the change
- Generates new authenticator QR code
- Previous email/SMS OTP request is superseded
Frontend Implementation
React Example (with Session Management)
import QRCode from 'qrcode.react';
function AuthenticatorSetup({ qrCodeUrl, secretKey, onComplete }) {
const [code, setCode] = useState('');
const [error, setError] = useState('');
const handleVerify = async () => {
try {
const response = await fetch('/api/BPMSelfService/commands/SelfAdminLoginCommand', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: currentUser.username,
password: currentUser.password,
verificationMethodType: 3,
setupVerificationCode: code
})
});
const result = await response.json();
if (result.status === 'success') {
onComplete(result.data.token);
} else {
setError('Invalid code. Please try again.');
}
} catch (err) {
setError('Setup failed. Please try again.');
}
};
return (
<div>
<h2>Set Up Authenticator App</h2>
<div className="qr-code">
<QRCode value={qrCodeUrl} size={256} />
</div>
<div className="manual-entry">
<p>Or enter this code manually:</p>
<code>{secretKey}</code>
</div>
<div className="verification">
<input
type="text"
placeholder="Enter 6-digit code"
value={code}
onChange={(e) => setCode(e.target.value)}
maxLength={6}
/>
<button onClick={handleVerify}>Verify</button>
</div>
{error && <div className="error">{error}</div>}
</div>
);
}
C# WPF Example
Implementation details removed for security.
Contact support for implementation guidance.
QR Code Format
The qrCodeUrl follows the standard otpauth:// URI format:
otpauth://totp/{Issuer}:{Account}?secret={Secret}&issuer={Issuer}
Example:
otpauth://totp/BankLingo:admin@example.com?secret=ABC123XYZ789&issuer=BankLingo
URI Components
- Protocol:
otpauth://totp/(Time-based OTP) - Issuer:
BankLingo(appears in authenticator app) - Account: User's email or username
- Secret: Base32-encoded secret key (160-bit)
- Issuer parameter: Redundant but recommended for compatibility
Technical Details
Secret Key
- Length: 160 bits (20 bytes)
- Encoding: Base32
- Format: 32 characters (A-Z, 2-7)
- Storage: Encrypted in database (
UserSecurity.TwoFASecretKey)
TOTP Algorithm
- Standard: RFC 6238
- Hash: HMAC-SHA1
- Digits: 6
- Period: 30 seconds
- Time Window: ±1 period (±30 seconds)
Security Considerations
Backup and Recovery
- Backup codes: Provide one-time backup codes
- Multiple devices: Allow registering multiple authenticators
- Recovery process: Admin-assisted recovery for lost devices
Best Practices
- ✅ Display both QR code and manual entry option
- ✅ Validate code immediately after scanning
- ✅ Store secret key encrypted
- ✅ Allow user to disable/reset authenticator
- ✅ Provide clear instructions and support
Troubleshooting
Common Issues
| Issue | Cause | Solution |
|---|---|---|
| Code always invalid | Time sync issue | Check device time, enable auto time sync |
| QR code won't scan | Low resolution/quality | Increase QR code size, improve lighting |
| Setup times out | User took too long | Regenerate QR code, start over |
| Can't find account in app | Scanning wrong code | Verify correct QR code is displayed |
| "Session expired" message | Returned after 24+ hours | Scan new QR code provided in response |
| "Previous session found" message | Resumed within 24 hours | Use code from previously scanned QR, or rescan |
| Switched methods but codes don't work | Method conflict | Complete setup with one method before switching |
What Happens If...?
I started setup but closed the app?
Within 24 hours: Return to login, select authenticator method again. You'll see "Previous setup session found" and can:
- Enter code from the QR you already scanned, OR
- Rescan the same QR code (it hasn't changed)
After 24 hours: You'll see "Your previous setup session expired." You must:
- Scan the NEW QR code provided
- The old QR code is no longer valid
I scanned the QR but haven't entered the code yet?
No problem! The session is preserved for 24 hours. When you return:
- Open your authenticator app
- Find the "BankLingo" entry you scanned earlier
- Enter the current 6-digit code
- Setup will complete successfully
I'm getting "Invalid code" errors after resuming?
Check these:
- Time sync: Ensure your device's clock is accurate (Settings → Date & Time → Set Automatically)
- Correct entry: Make sure you're looking at the "BankLingo" entry in your authenticator app
- Session expired: If it's been 24+ hours, you need the new QR code
- Code freshness: TOTP codes change every 30 seconds - enter the current code
Time Synchronization
TOTP codes are time-sensitive. Ensure:
- Device clock is accurate
- Auto time sync is enabled
- Time zone is correct
- NTP is working (for servers)
Related APIs
Implementation Reference
For technical implementation details, see: