ShareYourGroupShareYourGroupTwo-Factor Codes

TOTP for developers

The algorithm is short. Most mistakes are in what surrounds it.

What the RFCs require and recommend

Storing the secret

The verifier needs the secret itself, so it cannot be stored as a hash like a password. NIST says the keys held by verifiers must be strongly protected against unauthorized disclosure and limited to the software parts that need them. In practice: encrypt secrets at rest with a key kept outside the database, limit who can read them, and never write them into logs.

Test it

Use the vectors: RFC 4226 Appendix D for HOTP and RFC 6238 Appendix B for TOTP. Our page of RFC test vectors lists them and explains the longer seeds for SHA-256 and SHA-512. A test with a wrong code, a code from two steps ago and a reused code should fail.

Other points

  1. Compare codes in constant time where your language allows it.
  2. Show the QR code once, over https, and do not keep the image.
  3. Offer backup codes and a way to turn off the second step with proper checks.
  4. Plan for phishing. Codes can be relayed by a fake site in real time. If your users are at high risk, offer security keys or passkeys.
Should I allow codes from the previous step?

RFC 6238 recommends at most one step of delay. Many servers accept the previous and the next step to cover clock drift.

Can I use SHA-256 instead of SHA-1?

RFC 6238 allows it, but many authenticator apps support only SHA-1. Check your users' apps before you change it.

Sources

  • RFC 4226 (rfc-editor.org/rfc/rfc4226): requirement R4, R6 and throttling
  • RFC 6238 (rfc-editor.org/rfc/rfc6238): time-step size, validation, 2038
  • NIST SP 800-63B-4 (pages.nist.gov/800-63-4/sp800-63b.html): protection of OTP keys at the verifier

Read on 14 November 2026.

This is an independent guide. It is not connected with the IETF, NIST, CISA, any authenticator app or any company named here. Facts were read on 14 November 2026 from the documents named on each page; codes and examples were calculated with Python and with the page's own code, and checked against the RFC test vectors.