Loading…
Paste a base32 secret or an otpauth:// URI and get the rolling six-digit code with a countdown ring — computed in your browser, never sent anywhere.
Your files never leave your browser — all processing is local.
Loading…
A TOTP code is not magic: it is an HMAC of a shared secret and the current time, truncated to six digits, exactly as RFC 6238 specifies on top of the RFC 4226 HOTP construction. Both sides — the server that enrolled you and whatever generates your codes — hold the same base32 secret and run the same arithmetic over the same 30-second time step, which is why the codes agree without any network connection, and why a wrong clock is the single most common reason they don't. This page implements that arithmetic and nothing else, verified against the official test vectors published in the RFC appendices.
Be clear-eyed about what you are pasting. A TOTP secret is not like a password: it is the permanent seed from which every future code is derived, and anyone who holds it can generate valid codes forever, so handing it to a website that uploads it is handing over your second factor outright. This page is a static file with no server behind it — the code is computed by JavaScript in your tab, the secret never appears in a network request, and you can confirm that yourself in devtools. That is the reassuring half. The other half: storing secrets in this browser's localStorage is weaker than a dedicated authenticator on a locked phone. Unencrypted entries can be read by anything with access to this browser profile — an extension with the wrong permissions, malware on the machine, anyone at an unlocked session. That is exactly the risk the passphrase option exists to cover, and exactly why it is worth using.
With a passphrase set, entries are encrypted with AES-GCM before they touch localStorage, using a key derived from your passphrase with PBKDF2 — the browser's own WebCrypto implementation, no third-party crypto code. What sits on disk is ciphertext, useless without the passphrase, and the passphrase itself is never stored anywhere. The flip side is the whole point: forget the passphrase and the entries are gone. There is no recovery, no reset email, no back door — a back door for you would be a back door for everyone else too. Losing an entry costs you nothing but a re-enrolment: sign in to the service with a backup code and register the secret again.
To add an account, paste the base32 secret, paste a full otpauth:// URI, or scan the enrolment QR — a QR code is just that URI in visual form, and it is decoded locally like everything else. Accounts sit side by side, each with its code, a click-to-copy, and a countdown ring showing how far into the 30-second step you are. One habit worth having: if the ring is nearly empty, wait the second or two for the next code rather than racing the expiry — most servers accept one step either side, but a code typed just as it rolls over is the classic way to lose that race. The digits and algorithm are configurable — 6 or 8 digits, SHA-1, SHA-256 or SHA-512 — for the minority of services that stray from the defaults.
No, and you should not take that on faith — check it. Open your browser's devtools, switch to the Network tab, and add an account: no request carries the secret, and once the page has loaded you can generate codes with the network disconnected entirely. The page is a static file with no API behind it, so there is nothing to send the secret to even in principle. This matters more here than on almost any other tool, because a TOTP secret that has leaked once is compromised permanently — it cannot be rotated like a password, only replaced by re-enrolling the account.
Almost always the clock. TOTP divides Unix time into 30-second steps and both sides must land on the same step, so a device clock a minute off produces codes that are consistently valid — for the wrong moment. Most servers tolerate one step either side, about ±30 seconds of skew, per the RFC 6238 recommendation, so drift under half a minute usually goes unnoticed and anything beyond it starts failing. Fix the system clock — enable network time — rather than hunting for a bug in the secret. Then check the rarer causes: a secret copied with a typo (the base32 alphabet has no 0, 1, 8 or 9), or a service that uses 8 digits or SHA-256 while the entry is still on the defaults.
RFC 6238 defines TOTP over HMAC with SHA-1 as the default, and virtually every service uses exactly that with 6 digits and a 30-second step. SHA-1's known weakness is collision-finding, which does not translate into an attack on HMAC-SHA-1 — computing codes without the secret would require breaking HMAC itself, which nobody has done. So SHA-1 here is not the liability it is in certificate signatures. The settings exist because a minority of providers use SHA-256 or SHA-512, or issue 8-digit codes; if a service says so, or its otpauth:// URI carries algorithm= or digits= parameters, the entry has to match — a mismatched algorithm produces codes that are wrong every single time, not occasionally.
No, and it is not trying to. This is a convenience and a debugging tool: handy for checking a code at a desk, for verifying that a secret you are about to deploy actually produces the codes the server expects, or for inspecting what an otpauth:// URI contains. A dedicated authenticator on a phone keeps secrets in hardware-backed storage behind the device lock, survives browser data clearing, and is not exposed to browser extensions. Secrets in localStorage — even encrypted — do not get those protections. Keep your primary 2FA in a real authenticator with proper backups, and use this page for what a browser tool is good at.
No. That is the design, not an oversight: the entries are AES-GCM ciphertext, the key comes from your passphrase via PBKDF2, and without the passphrase nothing on this machine can decrypt them — no recovery flow, no reset. If recovery existed, it would be a mechanism anyone else could use too. The practical fix is quick: sign in to each service with your existing authenticator or a backup code and enrol again, which issues a fresh secret. This is also what backup codes are for — store them somewhere that is not this browser.
No. An enrolment QR code is nothing more than an otpauth:// URI drawn as pixels — the account name, the issuer and the base32 secret packed into a URL scheme. The camera frames are decoded by JavaScript running in your tab, the URI is parsed there too, and neither the image nor its contents appear in a network request — the same devtools check applies. Treat the QR itself with the respect the secret deserves, though: a screenshot of an enrolment QR is the secret, and it stays valid for as long as the account keeps that enrolment.
Need a strong password to go with that second factor? Generate one.
Inspecting a different kind of credential? Decode an X.509 certificate.
Verifying integrity rather than identity? Compute a file's hash instead.