TOTP 2FA QR Codes Explained
A technical deep-dive into the otpauth:// URI format, base32 secrets, and RFC 6238 TOTP math.
What is TOTP
TOTP (Time-based One-Time Password, RFC 6238) is the algorithm behind Google Authenticator, Authy, 1Password, Bitwarden, and every other authenticator app. It produces a 6-digit code that changes every 30 seconds based on a shared secret and the current Unix time.
The otpauth URI
otpauth://totp/Issuer:account@example.com?secret=BASE32SECRET&issuer=Issuer&algorithm=SHA1&digits=6&period=30The URI scheme is otpauth://, the type is totp (the other option is hotp, counter-based), then the label, then query parameters. Authenticator apps parse this URI and add it to the user's vault with one scan.
The label
Optional format: Issuer:account@example.com. The Issuer prefix is redundant with the issuer query param but most apps respect both.
The secret
A base32-encoded byte string (A-Z and 2-7, no padding). At least 128 bits recommended, most servers use 160 bits (32 base32 chars). Never URL-encode the secret; leave it as raw base32.
Algorithm, digits, period
algorithm: SHA1 (default, universally supported), SHA256, or SHA512. digits: 6 (default) or 8. period: 30 seconds (default) or 60. Stick with defaults unless you have a reason, some authenticator apps don't implement the non-default options.
How the code is computed
At each 30-second interval, the authenticator does: HMAC-SHA1(secret, floor(unix_time / 30)), takes the last 4 bits of the HMAC as a dynamic offset, reads 4 bytes starting at that offset, masks to 31 bits, and modulo-reduces by 106 to get a 6-digit code.
Use Abundera QR to generate one
Open the TOTP generator, fill in issuer (your brand name), account (the user's email or username), and a base32 secret. Defaults for algorithm/digits/period are correct for nearly every server. Download the PNG and email or SMS it to the user, or embed it in your enrollment flow.
Privacy note
Your TOTP secret is a shared credential between the server and the user. Abundera QR never sees either. All encoding happens in the browser. Read the full privacy policy.