Documentation menu
TOTP

Enrolling an authenticator

Generate a secret, show a QR code, store the secret yourself.

This page describes calls that never leave your server

Every helper here runs locally inside the SDK. There is no enrolment endpoint for TOTP, and nothing on this page consumes plan quota or appears in the developer portal.

The three steps

  1. 1Generate a random base32 secret.
  2. 2Persist it against your own user record, encrypted at rest.
  3. 3Render it as an otpauth:// URI and display it as a QR code.
main.go
secret, err := u2auth.GenerateSecret()
if err != nil {
    return err
}
// Store it against your own user, encrypted at rest.
if err := db.SaveTOTPSecret(userID, secret); err != nil {
    return err
}
uri := u2auth.GenerateQRCodeURI("Acme Corp", user.Email, secret)
// Render uri as a QR code for the user to scan.

Storing the secret

A TOTP secret is a permanent bearer credential: anyone holding it can mint valid codes for that user indefinitely, offline, without touching their phone. Treat it exactly as you would a password hash — encrypted at rest, never logged, never returned to the browser after enrolment.

Do not re-display a secret after enrolment

Once the user has scanned the QR code, there is no legitimate reason to show the secret again. A "reveal my secret" feature turns one compromised session into a permanent second-factor bypass. If a user loses their device, enrol a new secret instead.

Confirm before you trust it

Ask the user to type one code before you mark the factor as enabled, and verify it the same way you will at sign-in. Without that step a mis-scanned QR code locks the user out on their next login rather than during setup, when they can still fix it.

Recovery codes

Generate single-use recovery codes at the same time and store only their hashes. Because we hold no TOTP state, we cannot help a user who has lost their authenticator — recovery is entirely yours to design.

main.go
codes, err := u2auth.GenerateRecoveryCodes(10)
if err != nil {
    return err
}
// Store hashes, show the plaintext once, never again.

What goes in the URI

generateQRCodeURI builds a standard otpauth:// URI with your issuer name, the account label the user will see in their authenticator app, and the secret. Use a label the user recognises — their email or username — because it is what distinguishes your entry from every other one in their app.