Help us shape the future of Go! Take the 2026 Go Developer Survey

mfa

package
v0.5.3 Latest Latest
Warning

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

Go to latest
Published: Nov 3, 2026 License: MIT Imports: 33 Imported by: 0

Documentation

Overview

Package mfa owns typed TOTP/recovery factors and model-first factor management. Its manager rechecks passwords, locks models before factors, applies a separate MFA throttle, and persists replay/consumption with model changes and credential revocation. PostgreSQL stores encrypted TOTP secrets through explicit migrations. Management never issues authenticated credentials. TOTP uses six-digit HMAC-SHA1 with a 30-second period; inputs and results preserve model and factor types.

Index

Constants

View Source
const DefaultRecoveryCodes = 10
View Source
const MaxEnrollmentLifetime = time.Hour
View Source
const MaxPrune = 128
View Source
const MaxRecoveryCodes = 16
View Source
const MaxRotationBatch = 256

MaxRotationBatch bounds the factors examined by one ReencryptStale call.

Variables

This section is empty.

Functions

func ProvisioningURI

func ProvisioningURI(key TOTPSecret, issuer, account string) (secret.String, error)

ProvisioningURI constructs the otpauth URI for an authenticated enrollment response or local QR renderer. The URI contains the TOTP secret; never pass it to a remote QR-generation service, analytics or logging. Issuer/account labels must be nonempty, bounded, colon-free UTF-8 without control characters.

Types

type Action

type Action string

Action identifies an MFA security observation, never a credential or authority.

const (
	Enrolled            Action = "enrolled"
	Disabled            Action = "disabled"
	Verified            Action = "verified"
	RecoveryRegenerated Action = "recovery_regenerated"
	Reencrypted         Action = "reencrypted"
	Retired             Action = "retired"
	Rejected            Action = "rejected"
)

type Address

type Address struct {
	Namespace keyspace.Namespace
	Provider  auth.ProviderName
	Model     string
}

Address is the persistence boundary. A model/provider shares one TOTP factor across its guards; guard names do not create independent factor enrollments.

func (Address) Key

func (a Address) Key() (string, error)

Key validates the address and returns its opaque TOTP persistence scope.

func (Address) SubjectKey

func (a Address) SubjectKey(identity model.Identity) (string, error)

SubjectKey returns the subject's opaque key within the validated MFA address.

func (Address) Validate

func (a Address) Validate() error

Validate checks the namespace, provider and model address components.

type Backend

type Backend interface {
	// Within prepares the model lock, reads the factor and applies one change in an owned
	// transaction without retries.
	Within(context.Context, Address, model.Identity, func(context.Context, *database.Tx) error, func(context.Context, *database.Tx, value.Optional[Record], temporal.DateTime) (Change, error)) (value.Optional[Record], error)
	// Prune deletes at most limit expired pending factors without acquiring model locks.
	Prune(context.Context, Address, int) (uint64, error)
}

Backend owns one read-committed transaction. Within invokes prepare exactly once to lock the application model BEFORE reading/locking the factor record. That model lock serializes creation even when no factor row exists. It then calls change once with owned state and server time, validates its result and deadline, persists it and commits. Callback errors, panic/Goexit or cancellation roll back all database changes. Never suppress failures, detach callbacks, retry mutations or acquire model locks after factor/credential locks.

Callbacks use only the supplied transaction for database writes. The runtime may consult its bounded MFA lockout store before protected writes; external effects (email, publishing) require an outbox. After-commit failure may retain a Committed database outcome; an error is not permission to retry blindly. Prune deletes only expired pending factors and acquires no model locks.

type Change

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

Change explicitly selects replacement or removal. Zero is invalid. Before adds a strict pre-commit deadline, used to prevent late enrollment confirmation.

func Remove

func Remove() Change

Remove selects deletion of the current factor record.

func Replace

func Replace(record Record) Change

Replace selects replacement with an owned clone of record.

func (Change) Before

func (c Change) Before(until temporal.DateTime) Change

Before returns a change with a strict deadline to recheck before commit.

func (Change) Deadline

func (c Change) Deadline() value.Optional[temporal.DateTime]

Deadline returns the optional pre-commit deadline.

func (Change) Next

func (c Change) Next() value.Optional[Record]

Next returns an owned clone of the replacement record, or omission for a removal or undefined change.

func (Change) Removes

func (c Change) Removes() bool

Removes reports whether the change selects deletion.

func (Change) Validate

func (c Change) Validate(address Address, subject model.Identity) error

Validate checks the mutation choice, optional deadline and replacement's address and subject ownership.

type Config

type Config struct {
	Namespace          keyspace.Namespace
	Issuer             string
	EnrollmentLifetime time.Duration
	Window             int
	RecoveryCodes      int
	MaxConcurrent      int
	Timeout            time.Duration
}

Config declares the MFA namespace, issuer, enrollment and verification policy, and operation bounds.

func DefaultConfig

func DefaultConfig(namespace keyspace.Namespace, issuer string) Config

DefaultConfig returns default enrollment, TOTP-window, recovery-code and operation bounds for namespace and issuer.

func (Config) Validate

func (c Config) Validate() error

Validate checks namespace, issuer, enrollment lifetime, TOTP window, recovery-code count and operation bounds.

type Enrollment

type Enrollment[M any] struct {
	// contains filtered or unexported fields
}

Enrollment is returned only after the pending encrypted factor commits. Secret and URI are explicit disclosure boundaries for the enrolled account.

func (Enrollment[M]) CreatedAt

func (e Enrollment[M]) CreatedAt() temporal.DateTime

CreatedAt returns the enrollment's creation time.

func (Enrollment[M]) ExpiresAt

func (e Enrollment[M]) ExpiresAt() temporal.DateTime

ExpiresAt returns the pending enrollment's confirmation deadline.

func (Enrollment[M]) Format

func (Enrollment[M]) Format(s fmt.State, _ rune)

Format writes the label "MFA enrollment" without exposing the value.

func (Enrollment[M]) ID

func (e Enrollment[M]) ID() EnrollmentID[M]

ID returns the model-owned enrollment identifier.

func (Enrollment[M]) Secret

func (e Enrollment[M]) Secret() TOTPSecret

Secret exposes the enrollment's TOTP key for authenticated delivery.

func (Enrollment[M]) Subject

func (e Enrollment[M]) Subject() M

Subject returns the enrollment's model snapshot without cloning it.

func (Enrollment[M]) URI

func (e Enrollment[M]) URI() secret.String

URI exposes the secret provisioning URI for authenticated delivery.

type EnrollmentID

type EnrollmentID[M any] struct {
	// contains filtered or unexported fields
}

EnrollmentID preserves the model type. Parsing identifies a pending factor; it does not authenticate or authorize confirmation.

func ParseEnrollmentID

func ParseEnrollmentID[M any](text string) (EnrollmentID[M], error)

ParseEnrollmentID parses a nonzero enrollment identifier retaining model ownership.

func (EnrollmentID[M]) IsZero

func (id EnrollmentID[M]) IsZero() bool

IsZero reports whether the enrollment identifier is uninitialized.

func (EnrollmentID[M]) JSONContract

func (EnrollmentID[M]) JSONContract() contract.JSON[EnrollmentID[M]]

JSONContract describes the model-owned enrollment identifier as a UUID string.

func (EnrollmentID[M]) MarshalJSON

func (id EnrollmentID[M]) MarshalJSON() ([]byte, error)

MarshalJSON preserves the public enrollment identifier; it is not a secret or proof. The model owner remains part of its Go type and generated contract.

func (EnrollmentID[M]) String

func (id EnrollmentID[M]) String() string

String returns the enrollment identifier's text representation.

func (*EnrollmentID[M]) UnmarshalJSON

func (id *EnrollmentID[M]) UnmarshalJSON(data []byte) error

UnmarshalJSON clears the receiver and parses a bounded JSON string as a nonzero enrollment identifier.

type Factors

type Factors[M model.Identifiable, K any] struct {
	// contains filtered or unexported fields
}

Factors owns one model/provider's TOTP factor across guards. A separate typed MFA throttle is mandatory; never reuse the password-attempt declaration.

func New

func New[M model.Identifiable, K any](store *Store, provider auth.Provider[M, K], binding Model[M, K], attempts lockout.Throttle[K]) (*Factors[M, K], error)

New validates the store, provider, model callbacks and lockout throttle and creates a typed factor binding.

func (*Factors[M, K]) Confirm

func (f *Factors[M, K]) Confirm(ctx context.Context, password auth.PasswordResult[M, K], id EnrollmentID[M], code TOTPCode) (RecoveryCodes[M], error)

Confirm accepts only a TOTP code for the current unexpired enrollment. The accepted step, recovery hashes, model flag and all credential revocations commit together. The result is not an authenticated proof or a login response.

func (*Factors[M, K]) Disable

func (f *Factors[M, K]) Disable(ctx context.Context, password auth.PasswordResult[M, K], response Response) (M, error)

Disable requires a rechecked password, current TOTP/recovery factor and domain permission. Factor removal, model mutation and credential revocation share tx.

func (*Factors[M, K]) Enroll

func (f *Factors[M, K]) Enroll(ctx context.Context, password auth.PasswordResult[M, K]) (Enrollment[M], error)

Enroll replaces only an unconfirmed enrollment. The password result is rechecked under the model lock. An enabled/confirmed factor must be verified and disabled first; an ordinary session or bare model reference cannot enroll.

func (*Factors[M, K]) HasEnrolledFactor

func (f *Factors[M, K]) HasEnrolledFactor(_ context.Context, subject M) (bool, error)

HasEnrolledFactor reports the model's stored enabled state. It implements auth.EnrolledFactors for PasswordLogin.WithSecondFactor and performs no I/O.

func (*Factors[M, K]) Prune

func (f *Factors[M, K]) Prune(ctx context.Context, limit int) (uint64, error)

Prune removes a bounded batch of expired, unconfirmed enrollments only. Confirmed factors and application models are never deleted by maintenance.

func (*Factors[M, K]) Reencrypt

func (f *Factors[M, K]) Reencrypt(ctx context.Context, password auth.PasswordResult[M, K], response Response) error

Reencrypt requires password/factor verification, preserves the enrollment ID and recovery set (apart from the code used), and encrypts with the active key. It does not change MFA policy or issue credentials. Retain old keys until every stored factor has migrated; this operation cannot recover a lost key.

func (*Factors[M, K]) RegenerateRecovery

func (f *Factors[M, K]) RegenerateRecovery(ctx context.Context, password auth.PasswordResult[M, K], response Response) (RecoveryCodes[M], error)

RegenerateRecovery verifies a current factor, replaces the complete recovery set and revokes registered credentials atomically. No old recovery code survives.

func (*Factors[M, K]) RetireIn

func (f *Factors[M, K]) RetireIn(ctx context.Context, parent *database.Tx, reference model.Reference[M, K]) (M, error)

RetireIn is an explicitly authorized administrative operation for account retirement. It locks the existing model, removes its pending/confirmed factor, clears Enabled and invalidates every registered credential in the supplied transaction. Delete/retire the domain model in that same transaction AFTER this call. It accepts disabled accounts; it does not bypass a missing model or authenticate the caller. Enforce an administrative policy before invoking it.

Unlike Disable this does not require the retiring user's password/factor or CanDisable permission. Never expose it as an unauthenticated self-service endpoint. Returned state is provisional until outer commit. Rollback restores model, factor and credentials. Do not reuse account keys without retirement.

func (*Factors[M, K]) Validate

func (f *Factors[M, K]) Validate() error

Validate checks that the factor binding and store are configured.

func (*Factors[M, K]) Verifier

func (f *Factors[M, K]) Verifier(response Response) (auth.SecondFactor[M, K], error)

Verifier binds one typed TOTP/recovery response for Sessions.CompleteMFA or Tokens.CompleteMFA. It performs no I/O and grants no authority. Completion locks/resolves the current model once, checks its eligibility and enabled factor, consumes the pending credential and verifies the second factor in the credential creation transaction. A failure rolls back all provisional writes.

func (*Factors[M, K]) WithObserver

func (f *Factors[M, K]) WithObserver(observer Observer[M, K]) (*Factors[M, K], error)

WithObserver returns an immutable view. Register at most once; callbacks must respect cancellation and shared-state concurrency. Notices are not delivery receipts: outbox durability begins only when the enclosing transaction commits.

type Model

type Model[M model.Identifiable, K any] struct {
	Lock         func(context.Context, *database.Tx, K) (value.Optional[M], error)
	Enabled      func(M) bool
	SetEnabled   func(context.Context, *database.Tx, M, bool) (M, error)
	AccountLabel func(M) (string, error)
	CanDisable   func(context.Context, M) (bool, error)
	Invalidate   func(context.Context, *database.Tx, M) error
}

Model maps stored fields and normal generated mutations to MFA. Lock uses a generated ForUpdate query on the supplied transaction and preserves K. SetEnabled returns the complete updated model and must preserve identity. Invalidate joins tx (for example auth.Revocations.Invalidate) and revokes every registered credential guard. CanDisable enforces required-factor domain policy. Link the factors to login with PasswordLogin.WithSecondFactor(factors): an enrolled model then always requires MFA, whatever RequiresMFA returns. Callbacks must not commit, retry, send external messages or acquire other pools.

type Notice

type Notice[M any, K any] struct {
	// contains filtered or unexported fields
}

Notice exposes only the affected typed reference and action. It contains no model snapshot, password, factor secret, submitted code or credential. The initiator remains attribution.FromContext(ctx); affected subject is not actor.

func (Notice[M, K]) Action

func (n Notice[M, K]) Action() Action

Action returns the factor operation recorded in the notice.

func (Notice[M, K]) Format

func (Notice[M, K]) Format(s fmt.State, _ rune)

Format writes the label "MFA notice" without exposing the value.

func (Notice[M, K]) Subject

func (n Notice[M, K]) Subject() model.Reference[M, K]

Subject returns the notice's typed model reference.

type Observer

type Observer[M any, K any] struct {
	Changed  func(context.Context, *database.Tx, Notice[M, K]) error
	Rejected func(context.Context, Notice[M, K]) error
}

Observer maps MFA observations to application-owned event DTOs. Changed runs inside the mutation transaction: use events.Topic.Enqueue for durable dispatch, events.Topic.AfterCommit for process-local dispatch, or audit through this tx. Never perform external I/O there. Failure rolls back factor/model/credentials. Rejected is an immediate, process-local observation of a nonmatching factor or confirmed lockout. The enclosing mutation rolls back; do not persist a rejection through that transaction. No callback runs for malformed input, absent/inactive factors, missing models or infrastructure failures. Rejected errors remain causes of the denial and cannot turn it into success.

type Record

type Record struct {
	ID             model.ID[Record]
	Address        Address
	Subject        model.Identity
	Ciphertext     encryption.Ciphertext
	CreatedAt      temporal.DateTime
	PendingUntil   value.Optional[temporal.DateTime]
	ConfirmedAt    value.Optional[temporal.DateTime]
	LastStep       value.Optional[int64]
	RecoveryHashes []RecoveryHash
}

Record is the trusted storage-adapter boundary, never a transport DTO. A pending record has an expiry but no confirmation, replay step or recovery hashes. A confirmed record has no pending expiry. ID changes on reenrollment.

func (Record) Clone

func (r Record) Clone() Record

Clone returns a record with an independent recovery-hash slice.

func (Record) Format

func (Record) Format(s fmt.State, _ rune)

Format writes the label "stored MFA factor" without exposing the value.

func (Record) LogValue

func (Record) LogValue() slog.Value

LogValue returns a factor label without exposing stored secrets or metadata.

func (Record) MarshalJSON

func (Record) MarshalJSON() ([]byte, error)

MarshalJSON encodes the redaction marker without exposing the value.

func (Record) Validate

func (r Record) Validate(address Address, subject model.Identity) error

Validate checks address and subject ownership, encrypted data and pending or confirmed factor invariants.

type RecoveryCode

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

RecoveryCode is a distinct canonical 256-bit random factor encoded as 43 unpadded base64url characters. It is not a password, TOTP code or proof. Only the digest is persisted; consumption must be atomic with factor completion.

func ParseRecoveryCode

func ParseRecoveryCode(raw secret.String) (RecoveryCode, error)

ParseRecoveryCode checks recovery-secret syntax without verifying that the code is stored or unused.

func (RecoveryCode) Format

func (RecoveryCode) Format(s fmt.State, _ rune)

Format writes the redaction marker without exposing the value.

func (RecoveryCode) JSONContract

func (RecoveryCode) JSONContract() contract.JSON[RecoveryCode]

JSONContract describes recovery-code input as a JSON string; ordinary output remains redacted.

func (RecoveryCode) LogValue

func (RecoveryCode) LogValue() slog.Value

LogValue returns the redaction marker without exposing the value.

func (RecoveryCode) MarshalJSON

func (RecoveryCode) MarshalJSON() ([]byte, error)

MarshalJSON encodes the redaction marker without exposing the value.

func (RecoveryCode) Secret

func (c RecoveryCode) Secret() secret.String

Secret explicitly exposes the recovery-secret wrapper for delivery.

func (*RecoveryCode) UnmarshalJSON

func (c *RecoveryCode) UnmarshalJSON(data []byte) error

UnmarshalJSON clears the receiver and parses a bounded JSON string as a recovery code.

func (RecoveryCode) Validate

func (c RecoveryCode) Validate() error

Validate checks recovery-code syntax without verifying stored validity.

type RecoveryCodes

type RecoveryCodes[M any] struct {
	// contains filtered or unexported fields
}

RecoveryCodes exposes an owned slice only after the factor mutation commits. Ordinary serialization cannot disclose the private collection or model.

func (RecoveryCodes[M]) Codes

func (r RecoveryCodes[M]) Codes() []RecoveryCode

Codes returns a copy of the issued recovery-code slice for explicit delivery.

func (RecoveryCodes[M]) Format

func (RecoveryCodes[M]) Format(s fmt.State, _ rune)

Format writes the label "issued MFA recovery codes" without exposing the value.

func (RecoveryCodes[M]) ID

func (r RecoveryCodes[M]) ID() EnrollmentID[M]

ID returns the model-owned enrollment identifier associated with the codes.

func (RecoveryCodes[M]) Subject

func (r RecoveryCodes[M]) Subject() M

Subject returns the model snapshot associated with code issuance without cloning it.

type RecoveryHash

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

RecoveryHash is a redacted SHA-256 digest of a high-entropy RecoveryCode, sharing the credential-hash implementation used by sessions and tokens. Encoded is an explicit persistence boundary, never an authentication proof.

func ParseRecoveryHash

func ParseRecoveryHash(encoded string) (RecoveryHash, error)

ParseRecoveryHash restores a recovery digest from its hexadecimal persistence encoding.

func (RecoveryHash) Encoded

func (h RecoveryHash) Encoded() string

Encoded exposes the recovery digest's hexadecimal persistence representation.

func (RecoveryHash) Format

func (RecoveryHash) Format(s fmt.State, _ rune)

Format writes the redaction marker without exposing the value.

func (RecoveryHash) IsZero

func (h RecoveryHash) IsZero() bool

IsZero reports whether the recovery digest is uninitialized.

func (RecoveryHash) LogValue

func (RecoveryHash) LogValue() slog.Value

LogValue returns the redaction marker without exposing the value.

func (RecoveryHash) MarshalJSON

func (RecoveryHash) MarshalJSON() ([]byte, error)

MarshalJSON encodes the redaction marker without exposing the value.

type Response

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

Response is an explicit choice of TOTP or recovery factor. Enrollment confirmation accepts only TOTPCode and cannot receive this alternative.

func RecoveryResponse

func RecoveryResponse(code RecoveryCode) (Response, error)

RecoveryResponse validates code and selects recovery-code verification.

func TOTPResponse

func TOTPResponse(code TOTPCode) (Response, error)

TOTPResponse validates code and selects TOTP verification.

func (Response) Format

func (Response) Format(s fmt.State, _ rune)

Format writes the redaction marker without exposing the value.

func (Response) Validate

func (r Response) Validate() error

Validate requires exactly one supported factor response with valid syntax.

type Rotation

type Rotation struct {
	Reencrypted uint64
	Changed     uint64
	Failed      uint64
	Next        RotationCursor
	Done        bool
}

Rotation reports one bounded batch. Reencrypted counts committed replacements; Changed counts factors that changed concurrently (a later run re-examines them if still stale); Failed counts envelopes that could not be decrypted with the retained keys. Done reports that no stale factor remained after Next when the batch was read.

type RotationBackend

type RotationBackend interface {
	// StaleCiphertexts returns at most limit factors whose envelope was not
	// produced by the active key, ordered by Owner and strictly after the
	// cursor (empty starts from the beginning).
	StaleCiphertexts(ctx context.Context, active encryption.KeyID, after string, limit int) ([]StoredCiphertext, error)
	// ReplaceCiphertext stores next only while the factor still has the same
	// owner, generation and envelope. False means it changed concurrently.
	ReplaceCiphertext(ctx context.Context, stored StoredCiphertext, next encryption.Ciphertext) (bool, error)
}

RotationBackend is the storage contract for password-free key rotation across every address sharing the store. Implementations never load domain models.

type RotationCursor

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

RotationCursor resumes rotation after the last examined factor. The zero value starts from the beginning. It is opaque and not a model reference.

func (RotationCursor) IsZero

func (c RotationCursor) IsZero() bool

IsZero reports whether the cursor has no rotation owner.

type Store

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

Store borrows a transactional backend and immutable encryption keyring. One store bounds work across all model bindings. Construction performs no I/O.

func NewStore

func NewStore(backend Backend, keys *encryption.Keyring, config Config) (*Store, error)

NewStore validates and borrows the backend and keyring and creates bounded factor-operation admission.

func (*Store) ReencryptStale

func (s *Store) ReencryptStale(ctx context.Context, after RotationCursor, limit int) (Rotation, error)

ReencryptStale re-encrypts up to limit stored factors under the keyring's active key, for key rotation, without any user's password or domain model. Each replacement is conditional on the factor's generation and envelope, so it is safe alongside logins and management. The secret, generation, replay step and recovery hashes are unchanged, and no credential is revoked. Envelopes whose key is no longer retained are reported as Failed and skipped. Counts committed before an error are returned with it.

func (*Store) Validate

func (s *Store) Validate() error

Validate checks that the store is configured.

type StoredCiphertext

type StoredCiphertext struct {
	Owner      string
	Generation model.ID[Record]
	Ciphertext encryption.Ciphertext
}

StoredCiphertext is one stored factor envelope selected for key rotation. It carries the opaque subject key and factor generation that bind the envelope's encryption context, never a model, identity or plaintext.

type TOTPCode

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

TOTPCode preserves six ASCII digits, including leading zeroes. It is a typed unverified input. No trimming, Unicode digit conversion or truncation occurs.

func ParseTOTPCode

func ParseTOTPCode(raw secret.String) (TOTPCode, error)

ParseTOTPCode requires exactly six ASCII digits without checking their cryptographic validity.

func (TOTPCode) Format

func (TOTPCode) Format(s fmt.State, _ rune)

Format writes the redaction marker without exposing the value.

func (TOTPCode) JSONContract

func (TOTPCode) JSONContract() contract.JSON[TOTPCode]

JSONContract describes TOTP input as a JSON string; ordinary output remains redacted.

func (TOTPCode) LogValue

func (TOTPCode) LogValue() slog.Value

LogValue returns the redaction marker without exposing the value.

func (TOTPCode) MarshalJSON

func (TOTPCode) MarshalJSON() ([]byte, error)

MarshalJSON encodes the redaction marker without exposing the value.

func (TOTPCode) Secret

func (c TOTPCode) Secret() secret.String

Secret explicitly exposes the six-digit code wrapper.

func (*TOTPCode) UnmarshalJSON

func (c *TOTPCode) UnmarshalJSON(data []byte) error

UnmarshalJSON clears the receiver and parses a bounded JSON string as six ASCII digits.

func (TOTPCode) Validate

func (c TOTPCode) Validate() error

Validate checks six-digit syntax without verifying the code against a factor.

type TOTPSecret

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

TOTPSecret is a canonical unpadded uppercase base32 160-bit secret. Only Secret deliberately reveals it for encrypted persistence or authenticated enrollment delivery. Plain database storage and ordinary response serialization are not supported. This type does not promise memory erasure.

func GenerateTOTPSecret

func GenerateTOTPSecret() (TOTPSecret, error)

GenerateTOTPSecret creates a cryptographically random 160-bit key, returning fault.Internal if randomness fails.

func ParseTOTPSecret

func ParseTOTPSecret(encoded secret.String) (TOTPSecret, error)

ParseTOTPSecret requires a canonical unpadded uppercase base32 encoding of a 160-bit key.

func (TOTPSecret) Format

func (TOTPSecret) Format(s fmt.State, _ rune)

Format writes the redaction marker without exposing the value.

func (TOTPSecret) LogValue

func (TOTPSecret) LogValue() slog.Value

LogValue returns the redaction marker without exposing the value.

func (TOTPSecret) MarshalJSON

func (TOTPSecret) MarshalJSON() ([]byte, error)

MarshalJSON encodes the redaction marker without exposing the value.

func (TOTPSecret) Secret

func (s TOTPSecret) Secret() secret.String

Secret explicitly exposes the encoded key wrapper for encrypted persistence or enrollment delivery.

func (TOTPSecret) Validate

func (s TOTPSecret) Validate() error

Validate checks the key's canonical base32 encoding and length.

type TransactionalBackend

type TransactionalBackend interface {
	// WithinIn applies the model-before-factor mutation contract within the caller-owned
	// transaction, without committing it.
	WithinIn(context.Context, *database.Tx, Address, model.Identity, func(context.Context, *database.Tx) error, func(context.Context, *database.Tx, value.Optional[Record], temporal.DateTime) (Change, error)) (value.Optional[Record], error)
}

TransactionalBackend joins the credential creation transaction. It verifies exact pool ownership, uses a savepoint, restores schema settings and never commits. The same model-before-factor ordering and callback rules apply.

Directories

Path Synopsis
Package command supplies the explicit MFA key-rotation operation.
Package command supplies the explicit MFA key-rotation operation.
Package postgres stores encrypted MFA factors through explicit migrations and generated model queries.
Package postgres stores encrypted MFA factors through explicit migrations and generated model queries.

Jump to

Keyboard shortcuts

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