tfa

package module
v1.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 21, 2026 License: 0BSD Imports: 14 Imported by: 0

README

Two Factor Authentication

For a user, Two Factor Authentication (TFA or 2FA) makes things pretty simple. The user loads a QR code into their Authenticator app and they get codes every 30 seconds that helps them sign into their accounts. No network required. The short of it is that there's a ~32 byte secret stored in that QR. It goes through a fun HMAC process with that secret and how many 30 second intervals have passed since January 1, 1970 then uses certain bits of that output as an offset to pick enough bits to use to create a 6 digit number. Easy right? Well, hopefully it is now.

What goes on behind the scenes is that when you create an account at a website and you turn on TFA, you get ~32 cryptographically secure random bytes generated and tied to your account. Those bytes, through QR code, are entered into your authenticator app which constantly displays the latest time sensitive code. The website you're signing into is doing the same thing when you submit your code. They take your stored secret and the current time and generate the code. If it matches the user input then the user must have the same secret stored in their device. Sometimes, because of possible timing related issues, the website will generate multiple codes before and after the current time just in case your device is slightly slower or faster than the server.

This library has zero third-party dependencies — it uses only the Go standard library.

Installation

Use the go tool:

$ go get github.com/coreyog/tfa@latest

Setting Up 2FA for a User

When a user enables 2FA on their account, your server needs to generate a shared secret, store it, and give the user a way to load it into their authenticator app.

// Generate a cryptographically secure secret. 32 bytes is recommended.
// The minimum is 20 bytes per RFC 4226. Use 64 bytes if using SHA512.
secret, err := tfa.GenerateSecret(32)
if err != nil {
  log.Fatal(err)
}

// Base32-encode the secret for storage. This is the value you save in
// your database alongside the user's account.
b32secret := base32.StdEncoding.EncodeToString(secret)
db.SaveTFASecret(userID, b32secret)

// Generate an otpauth:// URL for the user to load into their
// authenticator app. The issuer is your app's name, and the name is
// typically the user's email or username. These are displayed in the
// authenticator app so the user can tell their accounts apart.
qrURL, err := tfa.GenerateQrUrl(secret, "MyApp", "user@example.com")
if err != nil {
  log.Fatal(err)
}

// Render qrURL as a QR code image and display it to the user. They scan
// it with Google Authenticator, Authy, or a similar app.

Verifying a Code at Login

When a user submits a 2FA code during login, retrieve their stored secret, decode it, and verify the code.

// Retrieve the base32-encoded secret you stored during enrollment.
b32secret, err := db.GetTFASecret(userID)
if err != nil {
  log.Fatal(err)
}

secret, err := base32.StdEncoding.DecodeString(b32secret)
if err != nil {
  log.Fatal(err)
}

// Verify the code the user typed in. The window parameter controls how
// many 30-second time steps to check in each direction. A window of 1
// means the current step plus one step before and one step after, which
// accounts for slight clock drift between your server and the user's
// device. A window of 1 is typical; 2 is generous.
ok, err := tfa.VerifyCode(secret, userInputCode, uint64(time.Now().Unix()), 1)
if err != nil {
  log.Fatal(err)
}

if ok {
  // Code is valid, proceed with login.
} else {
  // Code is invalid, reject the attempt.
}

Replay prevention: This library is stateless and does not track previously used codes. A valid code can be verified multiple times within the same time window. To prevent replay attacks, your application should record the last successfully authenticated time step for each user and reject any code at or before that step. See RFC 6238 Section 5.2.

Defaults

When no options are provided, the library uses these defaults:

Setting Default Notes
Algorithm SHA1 Widest compatibility with authenticator apps
Digits 6 Standard code length
Period 30s Standard time step duration

These match what most authenticator apps (Google Authenticator, Authy, etc.) expect, so most integrations won't need to change them.

Configurable Options

The library supports SHA1, SHA256, and SHA512 hash algorithms, 6 or 8 digit codes, and configurable time step periods. Most authenticator apps only support the defaults, so only change these if you have a specific requirement and have confirmed your authenticator app supports it.

When using non-default options, the same options must be passed to GenerateCode, VerifyCode, and GenerateQrUrl so they all agree on the parameters. GenerateQrUrl automatically includes non-default options in the URL so the authenticator app knows what to expect.

opts := []tfa.Option{
  tfa.WithAlgorithm(tfa.AlgorithmSHA256),
  tfa.WithDigits(8),
  tfa.WithPeriod(60),
}

// Generate a code with custom options.
code, err := tfa.GenerateCode(secret, uint64(time.Now().Unix()), opts...)

// Verify with the same options.
ok, err := tfa.VerifyCode(secret, code, uint64(time.Now().Unix()), 1, opts...)

// The QR URL will include algorithm=SHA256, digits=8, and period=60
// as query parameters so the authenticator app uses the right settings.
qrURL, err := tfa.GenerateQrUrl(secret, "MyApp", "user@example.com", opts...)

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GenerateCode

func GenerateCode(secret []byte, when uint64, opts ...Option) (string, error)

GenerateCode produces the TOTP code for the given secret and unix timestamp.

func GenerateQrUrl

func GenerateQrUrl(secret []byte, issuer string, name string, opts ...Option) (string, error)

GenerateQrUrl produces an otpauth:// URL suitable for encoding as a QR code that can be scanned by Google Authenticator or similar apps.

func GenerateSecret

func GenerateSecret(length int) ([]byte, error)

GenerateSecret creates a cryptographically secure random secret of the given length in bytes. The minimum length is 20 bytes per RFC 4226. Returns the raw bytes; use encoding/base32 to encode for storage or display.

func VerifyCode

func VerifyCode(secret []byte, code string, when uint64, window uint, opts ...Option) (bool, error)

VerifyCode checks if the provided code matches any time step within the given window. A window of 0 checks only the current step, a window of 1 checks ±1 step, etc. The maximum window is 100.

This function is stateless and does not track previously used codes. Callers must implement replay prevention by recording the last successfully authenticated time step for each user and rejecting codes at or before that step. See RFC 6238 Section 5.2.

Types

type Algorithm

type Algorithm int

Algorithm represents the HMAC hash algorithm used for code generation.

const (
	AlgorithmSHA1 Algorithm = iota
	AlgorithmSHA256
	AlgorithmSHA512
)

func (Algorithm) String

func (a Algorithm) String() string

String returns the canonical name of the algorithm as used in otpauth URIs.

type Option

type Option func(*config)

Option configures code generation and verification behavior.

func WithAlgorithm

func WithAlgorithm(a Algorithm) Option

WithAlgorithm sets the HMAC hash algorithm. Default is AlgorithmSHA1.

func WithDigits

func WithDigits(d int) Option

WithDigits sets the number of digits in the generated code. Must be between 6 and 10 inclusive. Default is 6.

func WithPeriod

func WithPeriod(p uint64) Option

WithPeriod sets the time step duration in seconds. Default is 30.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL