Skip to main content

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

EndpointMethodDescription
/api/v1/auth/totp/setup
POSTGenerate TOTP secret and QR code
/api/v1/auth/totp/verify
POSTVerify code and activate TOTP
/api/v1/auth/totp/status
GETCheck if TOTP is enabled
/api/v1/auth/totp/disable
DELETEDisable TOTP (requires current code)
/api/v1/auth/totp/backup-codes
POSTRegenerate backup codes
/api/v1/auth/totp/validate
POSTValidate 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

StatusErrorDescription
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

ParameterTypeRequiredDescription
token
stringYes6-digit code from authenticator app

Response

{ "success": true, "message": "TOTP has been successfully enabled" }

Error Responses

StatusErrorDescription
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

ParameterTypeRequiredDescription
token
stringYesCurrent 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

ParameterTypeRequiredDescription
token
stringYesCurrent 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

  1. Store backup codes securely - Prompt users to download or print backup codes immediately after setup

  2. Require TOTP for sensitive actions - Use TOTP verification for:

    • Changing passwords
    • Modifying security settings
    • Accessing sensitive data
  3. Show backup codes count - Display remaining backup codes in the UI so users know when to regenerate

  4. Handle clock skew - The ZewstID API accepts codes within a ±1 window (30 seconds) to handle clock drift

  5. Rate limit validation attempts - The API automatically rate-limits to prevent brute force attacks

TOTP vs Other MFA Methods

FeatureTOTPPush AuthWebAuthn
Offline capableYesNoYes
Phishing resistantNoYesYes
User experienceMediumBestBest
Setup complexityLowMediumLow
Hardware requiredNoPhoneAuthenticator

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

  1. Check time synchronization - Ensure the device clock is accurate
  2. Verify correct account - User may have multiple accounts in their app
  3. Wait for new code - Codes refresh every 30 seconds

User lost authenticator app

  1. User can log in with a backup code
  2. After login, disable TOTP from account settings
  3. 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

Was this page helpful?

Let us know how we can improve our documentation