TOTP Authenticator App Guide
Add Time-based One-Time Password (TOTP) authentication to your application.
Overview
TOTP authentication (commonly known as "Authenticator App") allows users to generate 6-digit codes using apps like Google Authenticator, Authy, or 1Password. This provides an additional layer of security beyond passwords.
How It Works
┌─────────────────┐ 1. User requests TOTP setup ┌──────────────┐ │ Your App │────────────────────────────────────▶│ ZewstID API │ │ │ │ │ │ User Portal │ 2. Secret + QR code returned │ Generates │ │ or Settings │◀────────────────────────────────────│ TOTP secret │ └─────────────────┘ └──────────────┘ │ │ 3. User scans QR │ with auth app ▼ ┌─────────────────┐ 4. User enters code ┌──────────────┐ │ Authenticator │ │ ZewstID API │ │ App │ 5. App verifies code │ │ │ │────────────────────────────────────▶│ Validates │ │ [523842] │ 6. TOTP enabled! │ and enables │ └─────────────────┘◀────────────────────────────────────└──────────────┘
Prerequisites
- ZewstID account with an active application
- Valid access token for authenticated users
API Endpoints
| Endpoint | Method | Description |
|---|---|---|
/api/v1/auth/totp/setup | POST | Generate TOTP secret and QR code |
/api/v1/auth/totp/verify | POST | Verify code and activate TOTP |
/api/v1/auth/totp/status | GET | Check if TOTP is enabled |
/api/v1/auth/totp/disable | DELETE | Disable TOTP (requires current code) |
/api/v1/auth/totp/backup-codes | POST | Regenerate backup codes |
/api/v1/auth/totp/validate | POST | Validate TOTP during login |
Step 1: Setup TOTP
Start the TOTP enrollment process for an authenticated user.
Request
curl -X POST https://api.zewstid.com/api/v1/auth/totp/setup \ -H "Authorization: Bearer <access_token>" \ -H "Content-Type: application/json"
Response
{ "secret": "JBSWY3DPEHPK3PXP", "qrcode": "data:image/png;base64,iVBORw0KGgo...", "backupCodes": [ "A1B2-C3D4", "E5F6-G7H8", "I9J0-K1L2", "M3N4-O5P6", "Q7R8-S9T0", "U1V2-W3X4", "Y5Z6-A7B8", "C9D0-E1F2", "G3H4-I5J6", "K7L8-M9N0" ], "message": "Scan the QR code with your authenticator app and verify with a code" }
Error Responses
| Status | Error | Description |
|---|---|---|
| 400 | totp_already_enabled | TOTP is already enabled for this account |
| 401 | unauthorized | Invalid or expired access token |
Step 2: Verify and Activate
After the user scans the QR code, verify with a code from their authenticator app.
Request
curl -X POST https://api.zewstid.com/api/v1/auth/totp/verify \ -H "Authorization: Bearer <access_token>" \ -H "Content-Type: application/json" \ -d '{"token": "523842"}'
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
token | string | Yes | 6-digit code from authenticator app |
Response
{ "success": true, "message": "TOTP has been successfully enabled" }
Error Responses
| Status | Error | Description |
|---|---|---|
| 400 | verification_failed | Invalid token. Please try again |
| 400 | invalid_request | Valid 6-digit token is required |
Step 3: Check Status
Check if TOTP is enabled for the user.
Request
curl -X GET https://api.zewstid.com/api/v1/auth/totp/status \ -H "Authorization: Bearer <access_token>"
Response
{ "enabled": true, "createdAt": "2026-01-15T10:30:00Z", "backupCodesRemaining": 10 }
Validate TOTP During Login
When TOTP is enabled, validate the code during the authentication flow.
Request
curl -X POST https://api.zewstid.com/api/v1/auth/totp/validate \ -H "Content-Type: application/json" \ -d '{ "userId": "user-uuid-here", "token": "523842" }'
Response
{ "valid": true }
Disable TOTP
Disable TOTP for a user (requires current code for verification).
Request
curl -X DELETE https://api.zewstid.com/api/v1/auth/totp/disable \ -H "Authorization: Bearer <access_token>" \ -H "Content-Type: application/json" \ -d '{"token": "523842"}'
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
token | string | Yes | Current 6-digit code from authenticator app |
Response
{ "success": true, "message": "TOTP has been disabled" }
Regenerate Backup Codes
Generate new backup codes (invalidates all existing codes).
Request
curl -X POST https://api.zewstid.com/api/v1/auth/totp/backup-codes \ -H "Authorization: Bearer <access_token>" \ -H "Content-Type: application/json" \ -d '{"token": "523842"}'
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
token | string | Yes | Current 6-digit code from authenticator app |
Response
{ "backupCodes": [ "A1B2-C3D4", "E5F6-G7H8", "I9J0-K1L2", "M3N4-O5P6", "Q7R8-S9T0", "U1V2-W3X4", "Y5Z6-A7B8", "C9D0-E1F2", "G3H4-I5J6", "K7L8-M9N0" ], "message": "New backup codes generated. Please store them securely" }
Using Backup Codes
Backup codes can be used instead of TOTP codes when the user loses access to their authenticator app. Each backup code can only be used once.
Request
curl -X POST https://api.zewstid.com/api/v1/auth/totp/validate \ -H "Content-Type: application/json" \ -d '{ "userId": "user-uuid-here", "token": "A1B2-C3D4" }'
The endpoint automatically detects if the code is a TOTP code or a backup code.
Frontend Implementation Example
React/Next.js Example
import { useState } from 'react'; function TOTPSetup() { const [step, setStep] = useState<'start' | 'verify' | 'backup' | 'done'>('start'); const [setupData, setSetupData] = useState(null); const [code, setCode] = useState(''); const [error, setError] = useState(''); const startSetup = async () => { const response = await fetch('/api/v1/auth/totp/setup', { method: 'POST', headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json', }, }); if (response.ok) { const data = await response.json(); setSetupData(data); setStep('verify'); } }; const verifyCode = async () => { const response = await fetch('/api/v1/auth/totp/verify', { method: 'POST', headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ token: code }), }); if (response.ok) { setStep('backup'); } else { const data = await response.json(); setError(data.error_description || 'Invalid code'); } }; if (step === 'start') { return ( <button onClick={startSetup}> Set Up Authenticator App </button> ); } if (step === 'verify') { return ( <div> <h3>Scan QR Code</h3> <img src={setupData.qrcode} alt="TOTP QR Code" /> <p>Or enter this code manually:</p> <code>{setupData.secret}</code> <h3>Enter Verification Code</h3> <input type="text" value={code} onChange={(e) => setCode(e.target.value)} maxLength={6} placeholder="000000" /> {error && <p className="error">{error}</p>} <button onClick={verifyCode}>Verify</button> </div> ); } if (step === 'backup') { return ( <div> <h3>Save Your Backup Codes</h3> <p>Store these codes in a secure place.</p> <ul> {setupData.backupCodes.map((code, i) => ( <li key={i}><code>{code}</code></li> ))} </ul> <button onClick={() => setStep('done')}> I've Saved My Codes </button> </div> ); } return <p>TOTP enabled successfully!</p>; }
React Native Example
import { useState } from 'react'; import { View, Text, TextInput, Button, Image, Alert } from 'react-native'; import { useZewstID } from '@zewstid/react-native'; function TOTPSetup() { const { accessToken } = useZewstID(); const [setupData, setSetupData] = useState(null); const [code, setCode] = useState(''); const startSetup = async () => { try { const response = await fetch( 'https://api.zewstid.com/api/v1/auth/totp/setup', { method: 'POST', headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json', }, } ); const data = await response.json(); setSetupData(data); } catch (err) { Alert.alert('Error', 'Failed to start TOTP setup'); } }; const verifyCode = async () => { try { const response = await fetch( 'https://api.zewstid.com/api/v1/auth/totp/verify', { method: 'POST', headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ token: code }), } ); if (response.ok) { Alert.alert('Success', 'TOTP enabled!'); } else { Alert.alert('Error', 'Invalid verification code'); } } catch (err) { Alert.alert('Error', 'Verification failed'); } }; if (!setupData) { return ( <View> <Button title="Set Up Authenticator" onPress={startSetup} /> </View> ); } return ( <View> <Text>Scan this QR code:</Text> <Image source={{ uri: setupData.qrcode }} style={{ width: 200, height: 200 }} /> <Text>Manual entry code: {setupData.secret}</Text> <TextInput value={code} onChangeText={setCode} keyboardType="numeric" maxLength={6} placeholder="Enter 6-digit code" /> <Button title="Verify" onPress={verifyCode} /> </View> ); }
Security Best Practices
-
Store backup codes securely - Prompt users to download or print backup codes immediately after setup
-
Require TOTP for sensitive actions - Use TOTP verification for:
- Changing passwords
- Modifying security settings
- Accessing sensitive data
-
Show backup codes count - Display remaining backup codes in the UI so users know when to regenerate
-
Handle clock skew - The ZewstID API accepts codes within a ±1 window (30 seconds) to handle clock drift
-
Rate limit validation attempts - The API automatically rate-limits to prevent brute force attacks
TOTP vs Other MFA Methods
| Feature | TOTP | Push Auth | WebAuthn |
|---|---|---|---|
| Offline capable | Yes | No | Yes |
| Phishing resistant | No | Yes | Yes |
| User experience | Medium | Best | Best |
| Setup complexity | Low | Medium | Low |
| Hardware required | No | Phone | Authenticator |
Supported Authenticator Apps
ZewstID TOTP is compatible with any RFC 6238-compliant authenticator:
- Google Authenticator
- Microsoft Authenticator
- Authy
- 1Password
- Bitwarden
- LastPass Authenticator
- Duo Mobile
- FreeOTP
Technical Details
- Algorithm: SHA1 (HMAC-SHA1)
- Digits: 6
- Period: 30 seconds
- Secret: 20 bytes (Base32 encoded)
- Time window: ±1 period (30 seconds tolerance)
Troubleshooting
"Invalid token" errors
- Check time synchronization - Ensure the device clock is accurate
- Verify correct account - User may have multiple accounts in their app
- Wait for new code - Codes refresh every 30 seconds
User lost authenticator app
- User can log in with a backup code
- After login, disable TOTP from account settings
- Set up TOTP again with new device
Backup codes exhausted
User must log in with their authenticator app to regenerate new backup codes.
FAQ
Q: What if a user loses both their phone and backup codes? A: Account recovery requires identity verification through your support process. Consider implementing account recovery flows.
Q: Can users have multiple authenticator apps? A: The same secret can be added to multiple apps, but we recommend using one primary app and backup codes.
Q: Is TOTP required for all users? A: TOTP is optional. You can enforce it via your application policies or ZewstID admin dashboard settings.
Q: How secure is TOTP? A: TOTP provides strong protection against password-based attacks but is not phishing-resistant. Consider WebAuthn for higher security needs.
Next Steps
- MFA Comparison Guide - Compare all MFA methods
- Push Authentication - Set up push notifications
- WebAuthn/Passkeys - Passwordless authentication
- API Reference - Complete API documentation
Was this page helpful?
Let us know how we can improve our documentation