TOTP secret generator
Generated in your browser · never sent, never stored
Time-based one-time passwords work from a secret both the server and the authenticator app hold. This generates that secret: 160 random bits, base32-encoded without padding, which is the form every authenticator app expects.
How to generate a TOTP secret
Why the defaults look dated and should stay
SHA-1, six digits and a thirty-second period are what RFC 6238 sets out and what every mainstream authenticator implements. The RFC does permit HMAC-SHA-256 and HMAC-SHA-512, and the URI format has parameters for digits and period, but the apps people actually have on their phones largely ignore anything but the defaults, so a non-default enrolment produces codes that never match and a support ticket nobody can diagnose from the server side. Nothing is lost by staying put. The attack this construction has to survive is guessing a six-digit code inside a thirty-second window, which is a rate-limiting problem rather than a hashing one, and the collision weaknesses that retired SHA-1 elsewhere do not touch HMAC.
The secret is a symmetric key, and both ends hold it
Unlike a password, which you can store as a one-way hash and never need back, a TOTP secret has to be recoverable by your server on every login, because the server recomputes the same code the phone shows. That makes it closer to an API key than to a credential: encrypt it at rest with a key held outside the database, keep it out of logs and out of error reports, and never return it from an API after enrolment. A leaked secrets table is a leaked second factor for every user in it, and rotating means re-enrolling every one of them by hand.
What the server has to do that this page cannot
Generating the secret is the easy half. Validation is where implementations go wrong. Accept a small window either side of the current step, because phone clocks drift and people take a second to type; RFC 6238 recommends allowing at most one step of network delay, so one step back and one forward is the usual choice and a wider window buys an attacker proportionally more guesses. Record the last step number a user successfully used and refuse anything at or before it, or a code stays replayable for the rest of its thirty seconds. Rate-limit attempts hard, since six digits is a million possibilities and an unthrottled endpoint is brute-forceable in an afternoon. Issue single-use recovery codes at enrolment, because a lost phone otherwise locks the account out permanently.
What the URI carries
The otpauth scheme is not an IETF standard; it is the Key URI format Google Authenticator defined and everyone else adopted. The label is the issuer and account joined by a colon, and the issuer is repeated as a query parameter because older apps read one and newer ones read the other. Alongside it go the secret, the algorithm, the digit count and the period. Nothing in the URI is secret except the secret itself, which is the whole of it, so a screenshot of the QR code is as good as the credential.
What people use it for
- Setting up two-factor authentication on a service you are building
- Producing the secret and the otpauth URI an enrolment screen has to show
- A throwaway secret for exercising a login flow’s second step in testing
- Rotating a shared secret after a database dump
- Checking what a valid otpauth URI looks like before writing the code that builds one
- Seeding a test fixture that needs a stable second factor
- Comparing your enrolment URI against a known-good one when an app refuses to scan it
Questions
RFC 4226 requires at least 128 bits and recommends 160. This generates 160, which is 32 base32 characters.
Because authenticator apps expect it stripped. RFC 4648 base32 pads to a multiple of eight characters with equals signs, and 160 bits divides into exactly 32 characters, so none are needed here in any case.
The browser’s cryptographic generator, through crypto.getRandomValues. It is the same source a server-side library would use, not a shuffled Math.random.
Because that is what RFC 6238 specifies and what authenticator apps implement. The known weaknesses in SHA-1 are collision attacks, which do not apply to HMAC in this construction.
The standard allows it, and RFC 6238 also permits SHA-256 and SHA-512, but most authenticator apps ignore non-default values and compute the code as though you had not set them. Stay with six digits and thirty seconds.
Render it as a QR code for the user to scan. The QR generator on this site will do that without sending it anywhere.
No. It is the Key URI format Google Authenticator published and the rest of the ecosystem copied. TOTP itself is RFC 6238; the way the secret gets into the app is convention.
Once in the label before the colon and once as a query parameter. Older apps read the label, later ones prefer the parameter, and writing both is what makes an enrolment display correctly everywhere.
One step back and one forward is the usual choice, and RFC 6238 recommends allowing at most one step for network delay. Every extra step multiplies the codes an attacker may guess at any moment.
Yes. Store the time step of the last accepted code per user and reject anything at or before it, otherwise a code intercepted early in its window stays valid for the rest of it.
Only with throttling. A million possibilities falls quickly to an endpoint that accepts unlimited attempts, so the digit count is not what protects the account; the attempt limit is.
Nothing recovers the secret, so plan for it before enrolment: issue a set of single-use recovery codes at the same time, store them hashed, and treat using one as a signal to re-enrol.
They should not. A shared secret means one leak compromises every account using it, and revoking one forces all of them to re-enrol. Generate a new one per enrolment.
No. It is generated in this page. If you paste it into a system that logs it, treat it as compromised and generate a new one.