The Complete Guide to TOTP for Teams

How time-based one-time passwords work, which properties of the algorithm cause every shared-account problem, and what a team needs to run TOTP.

TOTP takes two inputs and produces one output. The inputs are a shared secret and the current time; the output is a six-digit code that anyone holding the same secret can compute. There is no network call, no per-device registration, and no server-side record of which device produced a code.

Every difficulty teams have with two-factor authentication on shared accounts follows from that sentence. This guide explains the mechanism, then what it implies for a team of more than one person.

What is TOTP?

TOTP is specified in RFC 6238. It is a thin layer over HOTP (RFC 4226), which generates a code from a secret and a counter. HOTP increments the counter on each use; TOTP replaces the counter with the current time divided into fixed steps, so both sides can arrive at the same value without communicating.

How does TOTP work?

A generation looks like this:

counter = floor(unix_time / period)        # period is usually 30 seconds
digest  = hmac_sha1(secret, counter)       # 20 bytes
offset  = digest[-1] & 0x0F                # dynamic truncation
number  = int_from(digest[offset:offset+4]) & 0x7FFFFFFF
code    = number % 10 ** digits            # zero-padded, usually 6 digits

Four parameters are in play: the secret, the period (30 seconds by default), the number of digits (6), and the algorithm (SHA-1 in practice). The authenticator app on a phone does exactly the above, offline. The server does the same and compares.

Two consequences are worth stating plainly, because most misunderstandings start here:

  • Nothing about a code identifies its source. The server sees six digits that match, not which device or person produced them.
  • There is no state to revoke. Enrolling a device is not a registration; it is a copy of the secret. Nothing on the account records that it happened.

The secret is the account

The secret is typically 16 to 32 base32 characters. It does not expire, it is not tied to a device, and the protocol has no notion of rotating it.

When you scan a setup QR code, you are reading a URI in the format published as the Key URI Format:

otpauth://totp/Acme:[email protected]?secret=JBSWY3DPEHPK3PXP&issuer=Acme&algorithm=SHA1&digits=6&period=30

That string is the whole credential. The QR code is a picture of it. Which means a screenshot of the enrolment screen is not a convenience. It is the second factor, in a form that can be forwarded, backed up and indexed.

From this follow the three properties that make shared 2FA awkward:

  1. Possession is permanent access. Whoever has the secret can generate every future code, on any device, forever.
  2. Copies are invisible. No security page will ever tell you how many exist.
  3. Revocation means starting over. The only way to invalidate a copy is to disable 2FA on the account and enrol again, for everyone.

Enrolment, step by step

Knowing what happens during setup makes it obvious why some later choices are irreversible.

  1. The provider generates the secret. It is created server-side, stored against your account, and never changes again unless 2FA is disabled.
  2. It is presented once, as a QR code and usually as the base32 string behind a “can’t scan it?” link. This is the only moment the credential is displayed to a human.
  3. Your client stores it. An authenticator app writes it to the device; a team vault writes it to its own encrypted storage. Both are doing the same thing: keeping a copy.
  4. You submit one code to confirm. This proves the copy works. It is not a registration of the device: nothing about your phone is sent anywhere.
  5. The provider issues recovery codes, usually at this point, and usually only once.

Step 2 is the whole game. Whatever sees that screen holds the account’s second factor from then on, and step 4 gives no indication of how many things saw it. This is why “we’ll just screenshot it for now” is a decision with no expiry date, and why the useful question during setup is not who scans it but where the one copy is going to live.

Why the thirty-second window exists

Both sides need to agree on the counter without talking to each other, so they agree on time instead. A thirty-second step is the compromise: long enough to type a code, short enough that a captured code is worthless soon after.

Verifiers usually accept the immediately previous step as well, and sometimes the next one, to absorb typing delay and small clock differences. This is why a code often still works a few seconds after the countdown resets.

It also means clocks matter. A device whose clock is a minute off computes codes for a step the server has already left behind, and every code is rejected. When someone reports that codes stopped working, an unsynchronised clock is the first thing to check, before assuming the secret is wrong. Anything that generates codes on a team’s behalf needs a synchronised clock as a hard requirement, not a nice-to-have.

How verification works, and why a code should only work once

On the other side, the verifier computes the expected code for the current step and compares. Three details in that comparison matter to anyone running shared accounts.

The acceptance window is a deliberate trade-off. Each additional step a server accepts widens the time a captured code stays usable. One step back is normal; large windows are a sign of a service papering over clock problems.

A code should be single-use. RFC 6238 is explicit that a second authentication with the same time step should be refused, and good implementations track the last step used per account. The practical effect for a team: two people signing in within the same thirty seconds may find that the second attempt is rejected even though the code on screen is correct. Waiting for the next code is the fix, and it is not a bug.

Attempts should be throttled. Six digits is a million possibilities, which brute-force resists only if the server limits guesses. That protection lives entirely on the provider’s side; nothing you do with the secret improves it.

None of these are things a team configures, but all three explain symptoms that otherwise look like a broken setup: a code that works late, a code that works for one colleague and not the next, a login that starts refusing correct codes after a burst of attempts.

Is TOTP secure? What it protects against, and what it does not

It defends well against reused passwords, credential stuffing from breach dumps, and a password leaked in isolation. An attacker with the password alone cannot sign in.

It does not defend against a real-time relay: a convincing fake login page that asks for the code and forwards it within its window. TOTP codes are phishable by design, because the user can read them out. It also does nothing about malware on the device that generates them, a stolen session cookie, or a copied secret.

That last one is the team-relevant gap. Every other threat in the list is addressed by the account’s own security settings. A copied secret is addressed only by how your team handles custody, which is not something the provider can see or help with.

Where teams break TOTP

Four patterns, in ascending order of damage:

  • One person keeps the app. No secret is copied, but the account now depends on a human being’s availability.
  • A dedicated phone in a drawer. Fine in an office, useless remotely, and it records nothing.
  • The secret next to the password in a shared vault. Convenient, and it collapses two factors into one container.
  • The QR code posted in a channel. Permanent, uncountable distribution of the credential.

The trade-offs of each, and how to choose between them, are the subject of best ways to manage 2FA for shared accounts and, account type by account type, shared account 2FA: how should teams handle it?.

What a team-shaped TOTP setup requires

Working back from the mechanism, a setup that survives more than one person needs six things. This list is tool-agnostic: it is what to check, whatever you end up using.

Single custody of the secret. One place holds it, entered once, by whoever administers the account. Everything else consumes codes.

Code distribution without secret distribution. People who need to sign in get the current code and the countdown, and cannot recover the secret from what they see.

Authorisation per person and per account. Not per vault. The person who posts to the social profile has no reason to generate codes for the payment provider.

A record of code views. The provider’s log will show the shared account, not the person. A log of who requested a code is the only thing that narrows an incident to a name and a timestamp.

A synchronised clock wherever codes are generated, for the reason in the section above.

A tested recovery path. Which brings us to the part teams discover at the worst moment.

Backups and recovery

Two different things need to be recoverable, and they should not live in the same place as each other or as the account they protect.

The secret, because losing it means resetting 2FA on the account. Where it lives should be written down, and reachable by more than one administrator.

The provider’s recovery codes, which are single-use fallbacks issued at enrolment. They are for the day the secret is gone, not for daily access: they are finite, and nobody can tell which one was consumed by whom.

Storing the only copy of a recovery code inside the account it recovers is a loop that closes exactly when you need it open. Same for the secret of the account that guards the vault holding your secrets.

The recovery path is worth exercising once, deliberately, on a low-stakes account: disable 2FA with a recovery code, set it up again, confirm everyone who needs codes still has them. An untested recovery path is a plan you are choosing to discover under pressure.

Parameters you will actually meet

Defaults cover the overwhelming majority: SHA-1, six digits, thirty seconds. SHA-1’s use here is not the weakness it sounds like, since HMAC-SHA1 is not affected by the collision attacks that retired SHA-1 for signatures. The RFC allows SHA-256 and SHA-512, though app support is uneven enough that providers rarely use them.

A minority of services deviate: eight digits, a sixty-second period, or a non-numeric code alphabet. The practical consequence is a storage requirement. Whatever holds a secret must hold its digits, period and algorithm alongside it, or codes for those accounts will be wrong in a way that looks like a broken secret. Any tool that only accepts a bare secret string is quietly assuming the defaults.

A short glossary

Worth agreeing on inside a team, because these words get used interchangeably and then people talk past each other.

  • Secret (or seed): the base32 string the codes come from. Permanent.
  • Key: usually a synonym for the secret. Occasionally someone means the Key URI instead, which is a different thing.
  • Code (or OTP): the six digits. Valid for one step, single use.
  • Token: used loosely for both the six digits and, on dedicated hardware, the device holding the secret. Worth saying which one you mean.
  • HOTP: same construction with a counter that increments per use.
  • Period / time step: the interval a code is valid for, usually 30 seconds.
  • Drift: the gap between two clocks, which makes codes fail.
  • Key URI / otpauth URI: the text form of the enrolment payload; the QR code is its visual encoding.
  • Recovery codes: one-time fallbacks from the provider, unrelated to TOTP.
  • Enrolment: copying the secret onto a device. Not a registration, despite how it feels.

Where Share Auth fits

Share Auth implements the six requirements above for accounts that genuinely have a single login. Secrets are entered once and encrypted at rest, with digits, period and algorithm stored alongside them; members see codes and countdowns rather than secrets; permissions are per member; and every view is written to an access log. Removing a member removes their access to every account at once.

It is not a replacement for per-person access on platforms that offer it. Where a tool can give each teammate their own login and their own second factor, that is strictly better than sharing anything at all.

Checklist

  • Every shared account’s secret has one known home, reachable by two administrators.
  • Nobody needs the secret to obtain a code.
  • Access is granted per person, per account, and removable in one action.
  • Code views are logged as they happen.
  • Recovery codes are stored with the secret, outside the account they recover.
  • Clocks are synchronised wherever codes are generated.
  • The recovery path has been tested once, on purpose.
  • Any account whose QR code has ever been shared has had its 2FA reset.

Frequently asked questions

TOTP, or time-based one-time password, is the algorithm specified in RFC 6238 that turns a shared secret and the current time into a short code, usually six digits, valid for about thirty seconds. The account and the authenticator app hold the same secret, so both compute the same code without exchanging anything.

Read next · How TOTP works