TOTP QR Code for Authenticator Apps
Generate TOTP QR codes that authenticator apps can scan. Learn how otpauth:// URIs are encoded as QR codes and best practices for secure provisioning in your enrollment flow.
Detailed Explanation
Generating TOTP QR Codes
QR codes are the primary method for transferring TOTP secrets from a server to an authenticator app. The QR code encodes an otpauth:// URI that contains the secret and all configuration parameters.
How It Works
The enrollment flow follows these steps:
- Server generates a random secret and encodes it as Base32
- Server constructs an
otpauth://totp/...URI with the secret and parameters - The URI is rendered as a QR code on the enrollment page
- User scans the QR code with their authenticator app
- The app extracts and stores the secret, issuer, account name, and settings
Generating QR Codes
Server-side (Node.js with qrcode library):
const QRCode = require('qrcode');
const uri = 'otpauth://totp/MyApp:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=MyApp';
const dataUrl = await QRCode.toDataURL(uri, { width: 256 });
// Send dataUrl to client as <img src={dataUrl} />
Client-side (using a JavaScript QR library):
import { toDataURL } from 'qrcode';
const dataUrl = await toDataURL(uri, { errorCorrectionLevel: 'M', width: 256 });
QR Code Best Practices
- Size: minimum 256x256 pixels for reliable scanning
- Error correction: use level M (15% recovery) or Q (25%) for better scan reliability
- Contrast: black modules on white background — avoid colored or styled QR codes
- Quiet zone: maintain at least 4 modules of white space around the QR code
Security Considerations
The QR code contains the plaintext secret, so treat it as sensitive data:
- Never cache the QR code image on CDNs or in browser cache
- Generate on demand — create the QR code only during the enrollment session
- Use HTTPS — the page displaying the QR code must be served over TLS
- Expire the session — invalidate the enrollment page after a timeout or successful verification
- Show the secret as text too — some users prefer manual entry (provide a "Can't scan?" option)
Fallback: Manual Entry
Always provide the Base32 secret as copyable text alongside the QR code. Users may need manual entry when:
- Their camera is broken
- They are using a desktop authenticator
- The QR code fails to scan
- Screen reader accessibility is needed
Use Case
Frontend and backend developers building a 2FA enrollment page need to generate and display QR codes securely. This guide covers the full pipeline from URI construction to QR rendering, with important security considerations that prevent secret leakage. It is particularly relevant when designing enrollment UIs, implementing the 'Can't scan the code?' fallback, and ensuring the provisioning page meets security review requirements.