Engineering

🔧 Build a TOTP Authenticator in Go: Developer Guide 2026

By Ateeq Y Tanoli, · 3 June 2026 · 3 min read · 0 words

Once you've built your TOTP authenticator, pair it with 1Password to securely store your TOTP seeds and passwords together in one encrypted vault.

Generate a Free Strong Password →

More Password Security Tools

🔑 SecureKeyGen⚔️ TitanPasswords🛡️ Best Password Generator🔐 Free Strong Password⚡ Instant Password🗝️ Iron Vault Keys👨‍👩‍👧‍👦 Safe Pass Builder🛡️ Trusty Password⚙️ StrongPassFactory🔑 SecureKeyGen.org📚 TrustyPassword.org

Why Build Your Own TOTP Authenticator in Go?

Time-based One-Time Passwords (TOTP) power the six-digit codes that protect millions of accounts behind two-factor authentication. While apps like Google Authenticator and Authy are convenient, building your own TOTP authenticator in Go is one of the most rewarding ways to truly understand how modern authentication works under the hood. Go is an excellent language for this project because its standard library ships with everything you need: cryptographic hashing through crypto/hmac and crypto/sha1, base32 decoding, and precise time handling. In 2026, with security expectations higher than ever, knowing how to implement and audit your own one-time password logic is a valuable skill for any backend or security-minded developer.

The beauty of TOTP is that it is defined by an open standard, RFC 6238, which itself builds on the HOTP specification in RFC 4226. Because the algorithm is fully specified and deterministic, any authenticator that follows the spec will generate identical codes for the same secret and timestamp. This interoperability is what lets you build a tool that works seamlessly with every major web service that supports authenticator apps.

Understanding the TOTP Algorithm

Before writing any Go code, it helps to grasp the algorithm conceptually. TOTP is essentially HOTP with the counter replaced by the current time divided into fixed intervals. The process can be broken down into a handful of clear steps that every implementation must follow.

Each of these steps maps cleanly onto a function in Go, which makes the language a natural fit for a clean, testable implementation. The deterministic nature of the algorithm also means you can write robust unit tests against the official test vectors provided in the RFC documents.

Setting Up Your Go Project

Start by initializing a new Go module. Open a terminal in your project directory and run go mod init totp-authenticator. This creates the module file that tracks your dependencies. For a pure-from-scratch implementation you will only need the standard library, which keeps the dependency footprint at zero and the binary lightweight. If you prefer to lean on a battle-tested package for production, the community favorite github.com/pquerna/otp handles generation and validation, but writing the core yourself first will deepen your understanding immeasurably.

Organize your code into logical files. A common structure separates the cryptographic core from the command-line interface and storage layer. This separation of concerns keeps your project maintainable as it grows beyond a weekend prototype.

Decoding the Secret

Authenticator secrets arrive base32-encoded, often with spaces and lowercase letters that must be normalized before decoding. Go's encoding/base32 package handles the heavy lifting, but you should strip whitespace and convert to uppercase first. Many secrets also omit padding, so you will want to use the no-padding variant of the encoder or add padding manually. A small helper function that sanitizes input prevents a whole class of frustrating bugs where a valid secret is rejected because of formatting differences between providers.

Generating the Code

With a decoded secret in hand, computing the code is straightforward. You convert the time counter into a big-endian 8-byte array, feed it through HMAC-SHA1 with hmac.New(sha1.New, secret), and then apply dynamic truncation. The truncation offset comes from the lowest four bits of the final hash byte, and you read four bytes starting at that offset, masking off the highest bit to keep the result positive. Finally, you take the result modulo one million and format it with leading zeros using fmt.Sprintf("%06d", code).

This entire generation routine fits comfortably in around twenty lines of Go. The clarity of the resulting code is a testament to why so many security tools are written in Go: the language gets out of your way and lets the algorithm speak for itself.

Handling Time Windows and Validation

When validating a code on the server side, a naive comparison against the current interval will frustrate users whose clocks drift slightly. The standard solution is to check a small window of intervals, typically one step before and one step after the current time. This tolerance accommodates network latency and minor clock skew without meaningfully weakening security. Be careful, however, to track used codes so that a valid code cannot be replayed within its window, a subtle but important defense against interception attacks.

Storing Secrets Securely

A TOTP authenticator is only as trustworthy as its secret storage. Secrets are equivalent to long-lived passwords, so storing them in plaintext on disk defeats the purpose of the entire system. At minimum, encrypt the secret store with a key derived from a user passphrase using a memory-hard function like Argon2, available through golang.org/x/crypto/argon2. On desktop builds, consider integrating with the operating system keychain. The goal is to ensure that even if your storage file is exfiltrated, the secrets within remain useless to an attacker.

Building the Command-Line Interface

A practical authenticator needs a usable interface. Go's flag package is sufficient for simple tools, while libraries like Cobra provide richer subcommand structures for adding accounts, listing codes, and removing entries. A pleasant touch is to display a live countdown showing how many seconds remain before the current code expires, refreshing the terminal in place. This small detail transforms a bare script into something that feels like a polished application.

Testing Against Official Vectors

Never trust a cryptographic implementation that has not been verified against known-good outputs. RFC 6238 includes a table of test vectors mapping specific timestamps to expected codes for SHA1, SHA256, and SHA512 variants. Write table-driven tests in Go that assert your generator produces exactly these values. Passing these vectors gives you genuine confidence that your authenticator will interoperate correctly with every compliant service in the wild.

Going Further in 2026

Once your core authenticator works, several enhancements await. You might add support for SHA256 and SHA512 algorithms, variable digit counts, and custom time steps to handle services that deviate from defaults. Parsing otpauth:// URIs from QR codes lets you onboard accounts by scanning rather than typing. For teams, syncing an encrypted vault across devices adds convenience without sacrificing the zero-knowledge security model. By building this project in Go, you gain not only a working tool but a durable mental model of how time-based authentication keeps the modern web secure.

We use cookies to improve your experience. Learn more

Store and manage your passwords securely with NordPass.