Project 31, Security and auth
TOTP 2FA Authenticator
The six digit codes from authenticator apps are just math on a shared secret and the clock. This page does that math, live, for as many accounts as you like.
- Algorithm
- TOTP, RFC 6238
- Hash
- HMAC-SHA1 via Web Crypto
- Dependencies
- QR generator only
- Your browser
- Checking
Your codes
Click a code to copy it. Codes change every 30 seconds.
Add an account
Make a new secret
Verify a code
Checks the code for the first account, allowing one step of clock drift.
How it works
- Count time stepsThe current Unix time is divided by 30 to get a counter that changes every 30 seconds.
- Sign the counterThe counter, as 8 bytes, is signed with HMAC SHA-1 using the account's secret as the key.
- TruncateFour bytes are picked from the signature using its last nibble, turned into a number, and cut to 6 digits.
async function totp(base32Secret, step = 30, digits = 6) {
const counter = Math.floor(Date.now() / 1000 / step);
const msg = new DataView(new ArrayBuffer(8));
msg.setUint32(4, counter); // 8 byte big-endian counter
const key = await crypto.subtle.importKey("raw", base32Decode(base32Secret),
{ name: "HMAC", hash: "SHA-1" }, false, ["sign"]);
const h = new Uint8Array(await crypto.subtle.sign("HMAC", key, msg.buffer));
const o = h[19] & 0xf; // dynamic truncation
const n = ((h[o] & 0x7f) << 24) | (h[o + 1] << 16) | (h[o + 2] << 8) | h[o + 3];
return String(n % 10 ** digits).padStart(digits, "0");
}