# Totp.NewSecret, Totp.Uri, Totp.Verify, Totp.Code

> Authenticator-app codes (RFC 6238 — the six-digit, thirty-second codes every authenticator app shows), which is what a second factor is built on. `NewSecret` mints the secret, `Uri` is the address its QR code carries, and `Verify` checks a code and answers the step it matched, so a code used once is refused if it comes again.

<!-- id: function-totp · area: function · stability: preview · html: https://osysharp.com/reference/function/totp/ -->

## Summary        {#summary}
Three calls are the whole of an authenticator-app factor:

- `Totp.NewSecret()` — a fresh secret, in base32 (the form an app is given).
- `Totp.Uri(secret, issuer, account)` — the `otpauth://totp/…` address to show as a QR code (draw it with
  [`Qr.Encode`](https://osysharp.com/reference/function/qr-encode/)); `issuer` is your app's name in the phone's list, `account` which account it is.
- `Totp.Verify(secret, code, lastUsedStep)` — the **step** the code matched, or `-1`. Store the step and pass it back
  next time: a code for a step at or before it is refused, so a code cannot be used twice.
- `Totp.Code(secret)` — the code the phone shows now (`Totp.Code(secret, at)` at an instant): the device's half, which
  is how a test drives a second factor end to end.

If what you want is sign-in with a second factor, you do not need these directly — the
[UserAccounts kit](https://osysharp.com/reference/security/accounts-kit/) has it, pages and all.

## Signature      {#signature}
```osy syntax
Totp.NewSecret() -> string
Totp.Uri(<string> secret, <string> issuer, <string> account) -> string
Totp.Verify(<string> secret, <string> code, <long> lastUsedStep) -> long
Totp.Code(<string> secret) -> string
Totp.Code(<string> secret, <DateTime> at) -> string
```

## Description    {#description}

### What it speaks        {#flavour}
Exactly what the authenticator apps people carry speak: HMAC-SHA-1, **six digits**, a **thirty-second** step, and a
160-bit secret written in base32. `Verify` accepts the step before and after the current one (±30 s) for a phone whose
clock drifts, ignores spaces in the code, and treats anything that is not six digits as simply no match. It compares
in fixed time, so how long a wrong code takes says nothing about which digits were right.

### Refusing a replayed code        {#replay}
A code stays valid for up to ninety seconds, so a code read over a shoulder would work a second time inside that
window. `Verify` answers the step it matched; keep it on the account and hand it back:

```osy syntax
var step = Totp.Verify(secret, code, account.LastCodeStep);
if (step < 0) { return false; }          // wrong, or already used
account.LastCodeStep = step;             // the next code must be for a later step
```

### Keeping the secret        {#storing}
The secret IS the factor: whoever reads it can make every future code. Store it encrypted with
[`Crypto.Encrypt`](https://osysharp.com/reference/function/crypto-encrypt/) (the app's own platform-managed key), decrypt it only to verify, and
`deny read` the column from everyone but the flow that checks codes. `Uri` carries the secret too — show it once, to
the person enrolling, and never store it.

### Testing a flow that asks for a code        {#testing}
`Totp.Code` is what the phone would show, so a test can play the phone. Use it for the step it checks and no other:
check a code with `Verify`, never by comparing with `Code`. A code used once is refused next time, so a test that needs
two in a row asks for the next step's — `Totp.Code(key, DateTime.UtcNow.AddSeconds(30))` — which `Verify`'s ±1 window
accepts now.

### Why it runs on the server        {#server}
All four are server-only: a browser that could check a code would have to hold the secret the code proves
possession of. `NewSecret` draws from the host's RNG and `Verify` reads the clock, so a durable replay sees the first
run's answers.

## Examples       {#examples}
```osy title="enrol, then check codes without accepting one twice" test app=totp
entity Device {
  [MaxLength(400)] string Sealed;          // Crypto.Encrypt(secret)
  long LastStep = -1;
  security { allow read, create, update when IsAuthenticated; }
}

class Enrolment { public string Key = ""; public List<string> Qr = []; }

/// The key to type in and the QR code to scan — shown once.
Enrolment Start(string account) {
  var secret = Totp.NewSecret();
  new Device { Sealed = Crypto.Encrypt(secret) };
  return new Enrolment { Key = secret, Qr = Qr.Encode(Totp.Uri(secret, "Acme", account)) };
}

bool Check(Device device, string code) {
  var step = Totp.Verify(Crypto.Decrypt(device.Sealed), code, device.LastStep);
  if (step < 0) { return false; }
  device.LastStep = step;
  return true;
}
```

## See also       {#see-also}
- [The UserAccounts kit — sign-in done properly, once](https://osysharp.com/reference/security/accounts-kit/) — two-step sign-in, enrolment and recovery codes, built on these
- [Qr.Encode](https://osysharp.com/reference/function/qr-encode/) — drawing the enrolment QR code
- [Crypto.Encrypt and Crypto.Decrypt](https://osysharp.com/reference/function/crypto-encrypt/) — keeping the secret encrypted at rest
- [Security.* — hashing, verifying, tickets, random ids](https://osysharp.com/reference/stdlib/security/) — hashing, random ids and the rest of the credential surface
