totp

package
v0.6.4 Latest Latest
Warning

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

Go to latest
Published: Jun 13, 2024 License: Apache-2.0 Imports: 21 Imported by: 0

README

totp Supports:

  • Generating QR Code images for easy user enrollment
  • Time-based One-time Password Algorithm (TOTP) (RFC 6238): Time based OTP, the most commonly used method
  • HMAC-based One-time Password Algorithm (HOTP) (RFC 4226): Counter based OTP, which TOTP is based upon
  • Generation and Validation of codes for either algorithm

Implementing TOTP:

User Enrollment

For an example of a working enrollment work flow, GitHub has documented theirs, but the basics are:

  1. Generate new TOTP Key for a User. key,_ := totp.Generate(...).
  2. Display the Key's Secret and QR-Code for the User. key.Secret() and key.Image(...).
  3. Test that the user can successfully use their TOTP. totp.Validate(...).
  4. Store TOTP Secret for the User in your backend. key.Secret()
  5. Provide the user with "recovery codes". (See Recovery Codes bellow)

Code Generation

  • In either TOTP or HOTP cases, use the GenerateCode function and a counter or time.Time struct to generate a valid code compatible with most implementations.
  • For uncommon or custom settings, or to catch unlikely errors, use GenerateCodeCustom in either module.

Validation

  1. Prompt and validate User's password as normal.
  2. If the user has TOTP enabled, prompt for TOTP passcode.
  3. Retrieve the User's TOTP Secret from your backend.
  4. Validate the user's passcode. totp.Validate(...)

Recovery Codes

When a user loses access to their TOTP device, they would no longer have access to their account. Because TOTPs are often configured on mobile devices that can be lost, stolen or damaged, this is a common problem. For this reason many providers give their users "backup codes" or "recovery codes". These are a set of one time use codes that can be used instead of the TOTP. These can simply be randomly generated strings that you store in your backend. Github's documentation provides an overview of the user experience.

Documentation

Overview

Package totp provides code generation for TOTP (RFC 6238) and HOTP (RFC 4226)

Index

Constants

View Source
const (
	// OTPEmail allows a user to complete TFA with an OTP code delivered via email
	OTPEmail TFAOptions = "otp_email"
	// OTPPhone allows a user to complete TFA with an OTP code delivered via phone
	OTPPhone TFAOptions = "otp_phone"
	// TOTP allows a user to complete TFA with a TOTP device or application
	TOTP TFAOptions = "totp"
	// Phone is a delivery method for text messages
	Phone DeliveryMethod = "phone"
	// Email is a delivery method for email
	Email = "email"
	// OTPAddress is a message containing an OTP code for contact verification
	OTPAddress MessageType = "otp_address"
	// OTPResend is a message containing an OTP code
	OTPResend MessageType = "otp_resend"
	// OTPLogin is a message containing an OTP code for login
	OTPLogin MessageType = "otp_login"
	// OTPSignup is a message containing an OTP code for signup
	OTPSignup MessageType = "otp_signup"
)

Variables

View Source
var (
	// ErrCannotDecodeOTPHash is an error representing a failure to decode an OTP hash
	ErrCannotDecodeOTPHash = errors.New("cannot decode otp hash")

	// ErrInvalidOTPHashFormat is an error representing an invalid OTP hash format
	ErrInvalidOTPHashFormat = errors.New("invalid otp hash format")

	// ErrFailedToHashCode is an error representing a failure to hash code
	ErrFailedToHashCode = errors.New("failed to hash code")

	// ErrCipherTextTooShort is an error representing a ciphertext that is too short
	ErrCipherTextTooShort = errors.New("ciphertext too short")

	// ErrFailedToCreateCipherBlock is an error representing a failure to create a cipher block
	ErrFailedToCreateCipherBlock = errors.New("failed to create cipher block")

	// ErrCannotDecodeSecret is an error representing a failure to decode a secret
	ErrCannotDecodeSecret = errors.New("cannot decode secret")

	// ErrCannotWriteSecret is an error representing a failure to write a secret
	ErrCannotWriteSecret = errors.New("cannot write secret")

	// ErrFailedToDetermineSecretVersion is an error representing a failure to determine secret version
	ErrFailedToDetermineSecretVersion = errors.New("failed to determine secret version")

	// ErrFailedToCreateCipherText is an error representing a failure to create cipher text
	ErrFailedToCreateCipherText = errors.New("failed to create cipher text")

	// ErrNoSecretKeyForVersion is an error representing no secret key for version
	ErrNoSecretKeyForVersion = errors.New("no secret key for version")

	// ErrNoSecretKey is an error representing no secret key
	ErrNoSecretKey = errors.New("no secret key")

	// ErrFailedToValidateCode is an error representing a failure to validate code
	ErrFailedToValidateCode = errors.New("failed to validate code")

	// ErrCodeIsNoLongerValid is an error representing a code that is no longer valid
	ErrCodeIsNoLongerValid = errors.New("code is no longer valid")

	// ErrIncorrectCodeProvided is an error representing an incorrect code provided
	ErrIncorrectCodeProvided = errors.New("incorrect code provided")

	// ErrCannotDecryptSecret is an error representing a failure to decrypt secret
	ErrCannotDecryptSecret = errors.New("cannot decrypt secret")

	// ErrFailedToGetSecretForQR is an error representing a failure to get secret for qr
	ErrFailedToGetSecretForQR = errors.New("failed to get secret for qr")

	// ErrFailedToGenerateSecret is an error representing a failure to generate secret
	ErrFailedToGenerateSecret = errors.New("failed to generate secret")

	// ErrCannotHashOTPString is an error representing a failure to hash otp string
	ErrCannotHashOTPString = errors.New("cannot hash otp string")

	// ErrCannotGenerateRandomString is an error representing a failure to generate random string
	ErrCannotGenerateRandomString = errors.New("cannot generate random string")
)

Functions

func Bytes

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

Bytes returns securely generated random bytes

func BytesFromSample

func BytesFromSample(length int, samples ...string) ([]byte, error)

BytesFromSample returns securely generated random bytes from a string sample

func GenerateOTP

func GenerateOTP(secret string) (string, error)

GenerateOTP generates a Time-Based One-Time Password (TOTP).

func OTPHash

func OTPHash(s string) (string, error)

OTPHash returns a sha512 hash of a string

func String

func String(length int, samples ...string) (string, error)

String returns a securely generated random string from an optional sample

func StringB64

func StringB64(length int, samples ...string) (string, error)

StringB64 returns a securely generated random string

Types

type Config

type Config struct {
	// Enabled is a flag to enable or disable the OTP service
	Enabled bool `json:"enabled" koanf:"enabled" default:"true"`
	// CodeLength is the length of the OTP code
	CodeLength int `json:"codeLength" koanf:"codeLength" default:"6"`
	// Issuer is the issuer for TOTP codes
	Issuer string `json:"issuer" koanf:"issuer" default:"datum"`
	// WithRedis configures the service with a redis client
	WithRedis bool `json:"redis" koanf:"redis" default:"true"`
	// Secret stores a versioned secret key for cryptography functions
	Secret string `json:"secret" koanf:"secret"`
	// RecoveryCodeCount is the number of recovery codes to generate
	RecoveryCodeCount int `json:"recoveryCodeCount" koanf:"recoveryCodeCount" default:"16"`
	// RecoveryCodeLength is the length of a recovery code
	RecoveryCodeLength int `json:"recoveryCodeLength" koanf:"recoveryCodeLength" default:"8"`
}

type ConfigOption

type ConfigOption func(*OTP)

ConfigOption configures the validator

func WithCodeLength

func WithCodeLength(length int) ConfigOption

WithCodeLength configures the service with a length for random code generation

func WithIssuer

func WithIssuer(issuer string) ConfigOption

WithIssuer configures the service with a TOTP issuing domain

func WithRecoveryCodeCount

func WithRecoveryCodeCount(count int) ConfigOption

WithRecoveryCodeCount configures the service with a number of recovery codes to generate

func WithRecoveryCodeLength

func WithRecoveryCodeLength(length int) ConfigOption

WithRecoveryCodeLength configures the service with the length of recovery codes to generate

func WithRedis

func WithRedis(db otpRedis) ConfigOption

WithRedis configures the service with a redis client

func WithSecret

func WithSecret(x Secret) ConfigOption

WithSecret sets a new versioned Secret on the client

type DeliveryMethod

type DeliveryMethod string

DeliveryMethod represents a mechanism to send messages to users

type ErrCode

type ErrCode string

ErrCode is a machine readable code representing an error within the authenticator domain

type ErrInvalidCode

type ErrInvalidCode string

ErrInvalidCode represents an error related to an invalid TOTP/OTP code

func (ErrInvalidCode) Code

func (e ErrInvalidCode) Code() ErrCode

func (ErrInvalidCode) Error

func (e ErrInvalidCode) Error() string

func (ErrInvalidCode) Message

func (e ErrInvalidCode) Message() string

type Error

type Error interface {
	Error() string
	Message() string
	Code() ErrCode
}

Error represents an error within OTP/TOTP domain

type Hash

type Hash struct {
	CodeHash       string         `json:"code_hash"`
	ExpiresAt      int64          `json:"expires_at"`
	Address        string         `json:"address"`
	DeliveryMethod DeliveryMethod `json:"delivery_method"`
}

Hash contains a hash of a OTP code

func FromOTPHash

func FromOTPHash(otpHash string) (*Hash, error)

FromOTPHash parses an OTP hash string to individual parts

type Manager

type Manager struct {
	TOTPManager TOTPManager
}

Manager manages the protocol for SMS/Email 2FA codes and TOTP codes

type Message

type Message struct {
	// Type describes the classification of a Message
	Type MessageType
	// Subject is a human readable subject describe the Message
	Subject string
	// Delivery type of the message (e.g. phone or email)
	Delivery DeliveryMethod
	// Vars contains key/value variables to populate
	// templated content
	Vars map[string]string
	// Content of the message
	Content string
	// Delivery address of the user (e.g. phone or email)
	Address string
	// ExpiresAt is the latest time we can attempt delivery
	ExpiresAt time.Time
	// DeliveryAttempts is the total amount of delivery attempts made
	DeliveryAttempts int
}

Message is a message to be delivered to a user

type MessageType

type MessageType string

MessageType describes a classification of a Message

type OTP

type OTP struct {
	// contains filtered or unexported fields
}

OTP is a credential validator for User OTP codes

func (*OTP) GenerateRecoveryCodes

func (o *OTP) GenerateRecoveryCodes() []string

GenerateRecoveryCodes generates a list of recovery codes

func (*OTP) OTPCode

func (o *OTP) OTPCode(address string, method DeliveryMethod) (code string, hash string, err error)

OTPCode creates a random code and hash

func (*OTP) TOTPQRString

func (o *OTP) TOTPQRString(u *User) (string, error)

TOTPQRString returns a string containing account details for TOTP code generation

func (*OTP) TOTPSecret

func (o *OTP) TOTPSecret(u *User) (string, error)

TOTPSecret assigns a TOTP secret for a user for use in code generation. TOTP secrets are encrypted by a pre-configured secret key and decrypted only during validation. Encrypted keys are versioned to assist with migrations and backwards compatibility in the event an older secret ever needs to be deprecated.

func (*OTP) ValidateOTP

func (o *OTP) ValidateOTP(code string, hash string) error

ValidateOTP checks if a User's OTP code is valid users can input secrets sent by SMS or email (needs to be implemented separately)

func (*OTP) ValidateTOTP

func (o *OTP) ValidateTOTP(ctx context.Context, user *User, code string) error

ValidateTOTP checks if a User's TOTP is valid - first validate the TOTP against the user's secret key and then check if the code has been set in redis, indicating that it has been used in the past thirty seconds codes that have been validated are cached to prevent immediate reuse

type Secret

type Secret struct {
	Version int
	Key     string
}

Secret stores a versioned secret key for cryptography functions

type TFAOptions

type TFAOptions string

TFAOptions represents options a user may use to complete 2FA

type TOTPManager

type TOTPManager interface {
	// TOTPQRString returns a URL string used for TOTP code generation
	TOTPQRString(u *User) (string, error)
	// TOTPSecret creates a TOTP secret for code generation
	TOTPSecret(u *User) (string, error)
	// OTPCode creates a random OTP code and hash
	OTPCode(address string, method DeliveryMethod) (code, hash string, err error)
	// ValidateOTP checks if a User email/sms delivered OTP code is valid
	ValidateOTP(code, hash string) error
	// ValidateTOTP checks if a User TOTP code is valid
	ValidateTOTP(ctx context.Context, user *User, code string) error
	// GenerateRecoveryCodes creates a set of recovery codes for a user
	GenerateRecoveryCodes() []string
}

TOTPManager manages the protocol for SMS/Email 2FA codes and TOTP codes

func NewOTP

func NewOTP(options ...ConfigOption) TOTPManager

NewOTP returns a new OTP validator

type Token

type Token struct {
	// Phone is a User's phone number
	Phone string `json:"phone_number"`
	// CodeHash is the hash of a randomly generated code used
	// to validate an OTP code and escalate the token to an
	// authorized token
	CodeHash string `json:"code,omitempty"`
	// Code is the unhashed value of CodeHash. This value is
	// not persisted and returned to the client outside of the JWT
	// response through an alternative mechanism (e.g. Email). It is
	// validated by ensuring the SHA512 hash of the value matches the
	// CodeHash embedded in the token
	Code string `json:"-"`
	// TFAOptions represents available options a user may use to complete
	// 2FA.
	TFAOptions []TFAOptions `json:"tfa_options"`
}

Token is a token that provides proof of User authentication

type TokenState

type TokenState string

TokenState represents a state of a JWT token. A token may represent an intermediary state prior to authorization (ex. TOTP code is required)

type User

type User struct {
	// ID is a unique ID for the user
	ID string
	// Phone number associated with the account
	Phone sql.NullString
	// Email address associated with the account
	Email sql.NullString
	// TFASecret is a a secret string used to generate 2FA TOTP codes
	TFASecret string
	// IsPhoneAllowed specifies a user may complete authentication by verifying an OTP code delivered through SMS
	IsPhoneOTPAllowed bool
	// IsEmailOTPAllowed specifies a user may complete authentication by verifying an OTP code delivered through email
	IsEmailOTPAllowed bool
	// IsTOTPAllowed specifies a user may complete authentication by verifying a TOTP code
	IsTOTPAllowed bool
}

User represents a user who is registered with the service

func (*User) DefaultName

func (u *User) DefaultName() string

DefaultName returns the default name for a user (email or phone)

func (*User) DefaultOTPDelivery

func (u *User) DefaultOTPDelivery() DeliveryMethod

DefaultOTPDelivery returns the default OTP delivery method

Directories

Path Synopsis
api

Jump to

Keyboard shortcuts

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