Interactive demo

Multi-factor authentication

I implemented OTP and TOTP second factors across Attvest's financial platforms. This page re-implements TOTP (RFC 6238) with no libraries — just the Web Crypto API — and runs both halves in your browser: the authenticator that shows a code, and the server that decides whether to accept it.

  • RFC 6238
  • Web Crypto API
  • Clock-drift window
  • Replay protection
  • Lockout
Read the source, checked against the RFC 4226 and 6238 test vectors (opens in a new tab)
01

Authenticator

What the user's phone shows.

Current code—
Expires in 30 s
Shared secret (base32)
Generating…
Key URI

With SHA-1 and 6 digits, you can type this key into Google Authenticator or 1Password and it will show the same codes.

02

Server verification

Where the security decisions are made.

Try it

  • Clock drift: the previous and next codes are accepted too (±1 step), because phone clocks are rarely exact.
  • Replay protection: submit the same code twice — a code is single-use, even inside its 30 s window.
  • Brute-force limit: with only 106 possible codes, 5 wrong attempts lock verification for 30 s.
  • Constant-time comparison: every window is checked in full, so response time does not leak how close a guess was.

How a code is made

  1. Counter. T = floor(unix time ÷ 30), written as an 8-byte big-endian integer. JavaScript's bitwise operators are 32-bit, so it is written as two 32-bit halves.
  2. HMAC. HMAC-SHA-1(shared secret, counter).
  3. Dynamic truncation. The low 4 bits of the last digest byte pick a 4-byte window; its top bit is masked off to give a 31-bit integer.
  4. Reduce. That integer modulo 106, left-padded with zeros.

In production the secret lives only on the server and the phone, is stored encrypted, and replay and lockout state is persisted per user rather than held in memory.

Available for new opportunities

Have a role or a project in mind? Let’s talk.