Documentation
¶
Overview ¶
contract.go: Wires the mfa plugin into the forge-dashboard contract surface via plugin.ContractContributor.
Package mfa provides multi-factor authentication for AuthSome.
The MFA plugin supports TOTP (Time-based One-Time Password) enrollment and verification. It implements AfterSignIn to inject MFA challenges when users have MFA enabled, and provides HTTP routes for enrollment and verification.
Usage:
eng, _ := authsome.NewEngine(
authsome.WithStore(store),
authsome.WithPlugin(mfa.New(mfa.Config{
Issuer: "My App",
})),
)
Index ¶
- Constants
- Variables
- func GenerateSMSCode(length int) (string, error)
- func GenerateTOTPCode(secret string) (string, error)
- func GenerateTOTPCodeAt(secret string, at time.Time) (string, error)
- func GenerateTOTPKey(cfg TOTPConfig) (*otp.Key, error)
- func ValidateSMSCode(code string, challenge *SMSChallenge) bool
- func ValidateTOTP(code, secret string) bool
- func ValidateTOTPStep(code, secret string) (valid bool, step int64)
- func VerifyRecoveryCode(plaintext string, code *RecoveryCode) bool
- type ChallengeRequest
- type ChallengeResponse
- type Config
- type DisableRequest
- type DisableResponse
- type EncryptedStore
- func (s *EncryptedStore) ConsumeRecoveryCode(ctx context.Context, codeID id.RecoveryCodeID) error
- func (s *EncryptedStore) CreateEnrollment(ctx context.Context, e *Enrollment) error
- func (s *EncryptedStore) CreateRecoveryCodes(ctx context.Context, codes []*RecoveryCode) error
- func (s *EncryptedStore) DeleteEnrollment(ctx context.Context, mfaID id.MFAID) error
- func (s *EncryptedStore) DeleteRecoveryCodes(ctx context.Context, userID id.UserID) error
- func (s *EncryptedStore) GetEnrollment(ctx context.Context, userID id.UserID, method string) (*Enrollment, error)
- func (s *EncryptedStore) GetEnrollmentByID(ctx context.Context, mfaID id.MFAID) (*Enrollment, error)
- func (s *EncryptedStore) GetRecoveryCodes(ctx context.Context, userID id.UserID) ([]*RecoveryCode, error)
- func (s *EncryptedStore) ListEnrollments(ctx context.Context, userID id.UserID) ([]*Enrollment, error)
- func (s *EncryptedStore) UpdateEnrollment(ctx context.Context, e *Enrollment) error
- type EnrollRequest
- type EnrollResponse
- type Enrollment
- type MemoryStore
- func (s *MemoryStore) ConsumeRecoveryCode(_ context.Context, codeID id.RecoveryCodeID) error
- func (s *MemoryStore) CreateEnrollment(_ context.Context, e *Enrollment) error
- func (s *MemoryStore) CreateRecoveryCodes(_ context.Context, codes []*RecoveryCode) error
- func (s *MemoryStore) DeleteEnrollment(_ context.Context, mfaID id.MFAID) error
- func (s *MemoryStore) DeleteRecoveryCodes(_ context.Context, userID id.UserID) error
- func (s *MemoryStore) GetEnrollment(_ context.Context, userID id.UserID, method string) (*Enrollment, error)
- func (s *MemoryStore) GetEnrollmentByID(_ context.Context, mfaID id.MFAID) (*Enrollment, error)
- func (s *MemoryStore) GetRecoveryCodes(_ context.Context, userID id.UserID) ([]*RecoveryCode, error)
- func (s *MemoryStore) ListEnrollments(_ context.Context, userID id.UserID) ([]*Enrollment, error)
- func (s *MemoryStore) UpdateEnrollment(_ context.Context, e *Enrollment) error
- type MongoStore
- func (s *MongoStore) ConsumeRecoveryCode(ctx context.Context, codeID id.RecoveryCodeID) error
- func (s *MongoStore) CreateEnrollment(ctx context.Context, e *Enrollment) error
- func (s *MongoStore) CreateRecoveryCodes(ctx context.Context, codes []*RecoveryCode) error
- func (s *MongoStore) DeleteEnrollment(ctx context.Context, mfaID id.MFAID) error
- func (s *MongoStore) DeleteRecoveryCodes(ctx context.Context, userID id.UserID) error
- func (s *MongoStore) GetEnrollment(ctx context.Context, userID id.UserID, method string) (*Enrollment, error)
- func (s *MongoStore) GetEnrollmentByID(ctx context.Context, mfaID id.MFAID) (*Enrollment, error)
- func (s *MongoStore) GetRecoveryCodes(ctx context.Context, userID id.UserID) ([]*RecoveryCode, error)
- func (s *MongoStore) ListEnrollments(ctx context.Context, userID id.UserID) ([]*Enrollment, error)
- func (s *MongoStore) UpdateEnrollment(ctx context.Context, e *Enrollment) error
- type Plugin
- func (p *Plugin) DeclareSettings(m *settings.Manager) error
- func (p *Plugin) HasMFA(ctx context.Context, userID id.UserID) bool
- func (p *Plugin) MigrationGroups(driverName string) []*migrate.Group
- func (p *Plugin) Name() string
- func (p *Plugin) OnBeforeUserDelete(ctx context.Context, userID id.UserID) error
- func (p *Plugin) OnInit(_ context.Context, engine plugin.Engine) error
- func (p *Plugin) RegisterContract(d *dispatcher.Dispatcher, reg contract.Registry, wreg contract.WardenRegistry, ...) error
- func (p *Plugin) RegisterRoutes(router forge.Router) error
- func (p *Plugin) SetSMSSender(s bridge.SMSSender)
- func (p *Plugin) SetStore(s Store)
- type PostgresStore
- func (s *PostgresStore) ConsumeRecoveryCode(ctx context.Context, codeID id.RecoveryCodeID) error
- func (s *PostgresStore) CreateEnrollment(ctx context.Context, e *Enrollment) error
- func (s *PostgresStore) CreateRecoveryCodes(ctx context.Context, codes []*RecoveryCode) error
- func (s *PostgresStore) DeleteEnrollment(ctx context.Context, mfaID id.MFAID) error
- func (s *PostgresStore) DeleteRecoveryCodes(ctx context.Context, userID id.UserID) error
- func (s *PostgresStore) GetEnrollment(ctx context.Context, userID id.UserID, method string) (*Enrollment, error)
- func (s *PostgresStore) GetEnrollmentByID(ctx context.Context, mfaID id.MFAID) (*Enrollment, error)
- func (s *PostgresStore) GetRecoveryCodes(ctx context.Context, userID id.UserID) ([]*RecoveryCode, error)
- func (s *PostgresStore) ListEnrollments(ctx context.Context, userID id.UserID) ([]*Enrollment, error)
- func (s *PostgresStore) UpdateEnrollment(ctx context.Context, e *Enrollment) error
- type RecoveryCode
- type RecoveryRegenerateRequest
- type RecoveryRegenerateResponse
- type RecoveryVerifyRequest
- type RecoveryVerifyResponse
- type SMSChallenge
- type SMSSendRequest
- type SMSSendResponse
- type SMSVerifyRequest
- type SMSVerifyResponse
- type SqliteStore
- func (s *SqliteStore) ConsumeRecoveryCode(ctx context.Context, codeID id.RecoveryCodeID) error
- func (s *SqliteStore) CreateEnrollment(ctx context.Context, e *Enrollment) error
- func (s *SqliteStore) CreateRecoveryCodes(ctx context.Context, codes []*RecoveryCode) error
- func (s *SqliteStore) DeleteEnrollment(ctx context.Context, mfaID id.MFAID) error
- func (s *SqliteStore) DeleteRecoveryCodes(ctx context.Context, userID id.UserID) error
- func (s *SqliteStore) GetEnrollment(ctx context.Context, userID id.UserID, method string) (*Enrollment, error)
- func (s *SqliteStore) GetEnrollmentByID(ctx context.Context, mfaID id.MFAID) (*Enrollment, error)
- func (s *SqliteStore) GetRecoveryCodes(ctx context.Context, userID id.UserID) ([]*RecoveryCode, error)
- func (s *SqliteStore) ListEnrollments(ctx context.Context, userID id.UserID) ([]*Enrollment, error)
- func (s *SqliteStore) UpdateEnrollment(ctx context.Context, e *Enrollment) error
- type Store
- type TOTPConfig
- type VerifyMFARequest
- type VerifyMFAResponse
Constants ¶
const DefaultRecoveryCodeCount = 8
DefaultRecoveryCodeCount is the number of recovery codes generated per enrollment.
Variables ¶
var ErrEnrollmentNotFound = errors.New("mfa: enrollment not found")
ErrEnrollmentNotFound is returned when an MFA enrollment is not found.
var PostgresMigrations = migrate.NewGroup("authsome-mfa", migrate.DependsOn("authsome"))
PostgresMigrations is the postgres migration group for the MFA plugin.
var ( // SettingIssuer controls the issuer name shown in authenticator apps. SettingIssuer = settings.Define("mfa.issuer", "AuthSome", settings.WithDisplayName("Issuer Name"), settings.WithDescription("Name shown in authenticator apps (e.g. Google Authenticator)"), settings.WithCategory("Multi-Factor Auth"), settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp), settings.WithPlaceholder("AuthSome"), settings.WithHelpText("The issuer label users see in their authenticator app"), settings.WithOrder(10), ) )
var SqliteMigrations = migrate.NewGroup("authsome-mfa", migrate.DependsOn("authsome"))
SqliteMigrations is the SQLite migration group for the MFA plugin.
Functions ¶
func GenerateSMSCode ¶ added in v1.9.0
GenerateSMSCode generates a random numeric code of the given length.
func GenerateTOTPCode ¶ added in v1.9.0
GenerateTOTPCode generates a current TOTP code for a given secret. Primarily useful for testing.
func GenerateTOTPCodeAt ¶ added in v1.9.0
GenerateTOTPCodeAt generates the TOTP code for a given secret at a specific time. Primarily useful for testing flows that need two codes from different time-steps — a code is single-use within its acceptance window, so a step-up check cannot reuse the code that completed an earlier step.
Only offsets within the ±1-step acceptance window (see totpSkew) will still validate; ±30s is the usable distance from now.
func GenerateTOTPKey ¶ added in v1.9.0
func GenerateTOTPKey(cfg TOTPConfig) (*otp.Key, error)
GenerateTOTPKey creates a new TOTP secret key.
func ValidateSMSCode ¶ added in v1.9.0
func ValidateSMSCode(code string, challenge *SMSChallenge) bool
ValidateSMSCode checks whether the provided code matches the challenge and is not expired.
func ValidateTOTP ¶ added in v1.9.0
ValidateTOTP validates a TOTP code against a secret.
func ValidateTOTPStep ¶ added in v1.9.0
ValidateTOTPStep validates a code and, on success, returns the time-step (unix seconds / period) the code corresponds to. The step uniquely identifies the code within its validity window, letting callers reject a replay of the same code. Acceptance matches ValidateTOTP (±1 step).
func VerifyRecoveryCode ¶ added in v1.9.0
func VerifyRecoveryCode(plaintext string, code *RecoveryCode) bool
VerifyRecoveryCode checks if the given plaintext code matches the hashed code.
Types ¶
type ChallengeRequest ¶
type ChallengeRequest struct {
Ticket string `json:"mfa_ticket" description:"Ticket returned by /v1/signin when MFA was required"`
Code string `json:"code" description:"TOTP code from the user's authenticator (6 digits) or recovery code"`
}
ChallengeRequest is the request body for MFA challenge during the sign-in MFA gate. The mfa_ticket is what /v1/signin (or any other session-issuing endpoint) returned in the 403 body when MFA was required; the code is the user's TOTP digits or recovery code.
type ChallengeResponse ¶
type ChallengeResponse struct {
User any `json:"user" description:"User object"`
SessionToken string `json:"session_token" description:"Session token"`
RefreshToken string `json:"refresh_token" description:"Refresh token"`
ExpiresAt string `json:"expires_at" description:"Token expiration time (RFC3339)"`
}
ChallengeResponse is the AuthResponse-shaped body returned on a successful MFA challenge. The shape matches /v1/signin's success response so SDK consumers can persist a session via the same code path regardless of whether the MFA gate fired.
type Config ¶
type Config struct {
// Issuer is the name shown in authenticator apps (default: "AuthSome").
Issuer string
// StepUpWindow is how recently the caller must have signed in to enrol
// a first factor on session auth alone (default: 5 minutes). Once a
// factor is verified, enrolling another one asks for that factor.
StepUpWindow time.Duration
}
Config configures the MFA plugin.
type DisableRequest ¶ added in v1.9.0
type DisableRequest struct {
// Code re-proves possession of the second factor: a current TOTP code or
// an unused recovery code. Required in practice, but optional to the
// binder so an unauthenticated caller still gets 401 rather than a 400
// that reports a missing field before authentication was even checked.
Code string `json:"code,omitempty"`
}
DisableRequest is an empty request for disabling MFA.
type DisableResponse ¶ added in v1.9.0
type DisableResponse struct {
Status string `json:"status"`
}
DisableResponse is returned when MFA is disabled.
type EncryptedStore ¶ added in v1.9.0
type EncryptedStore struct {
// contains filtered or unexported fields
}
EncryptedStore wraps a Store, transparently encrypting the TOTP/SMS secret on an Enrollment before it reaches the underlying storage and decrypting it on the way back out.
It is safe over a store that already holds legacy plaintext secrets: bridge.AESGCMEncryptor's Decrypt returns input unchanged when no envelope prefix is present. Recovery codes are already hashed by the caller, so those methods delegate without modification.
func NewEncryptedStore ¶ added in v1.9.0
func NewEncryptedStore(inner Store, enc bridge.Encryptor) *EncryptedStore
NewEncryptedStore wraps inner with at-rest secret encryption. A nil encryptor falls back to bridge.NoopEncryptor (no encryption).
func (*EncryptedStore) ConsumeRecoveryCode ¶ added in v1.9.0
func (s *EncryptedStore) ConsumeRecoveryCode(ctx context.Context, codeID id.RecoveryCodeID) error
func (*EncryptedStore) CreateEnrollment ¶ added in v1.9.0
func (s *EncryptedStore) CreateEnrollment(ctx context.Context, e *Enrollment) error
func (*EncryptedStore) CreateRecoveryCodes ¶ added in v1.9.0
func (s *EncryptedStore) CreateRecoveryCodes(ctx context.Context, codes []*RecoveryCode) error
Recovery codes are hashed by the caller — delegate unchanged.
func (*EncryptedStore) DeleteEnrollment ¶ added in v1.9.0
func (*EncryptedStore) DeleteRecoveryCodes ¶ added in v1.9.0
func (*EncryptedStore) GetEnrollment ¶ added in v1.9.0
func (s *EncryptedStore) GetEnrollment(ctx context.Context, userID id.UserID, method string) (*Enrollment, error)
func (*EncryptedStore) GetEnrollmentByID ¶ added in v1.9.0
func (s *EncryptedStore) GetEnrollmentByID(ctx context.Context, mfaID id.MFAID) (*Enrollment, error)
func (*EncryptedStore) GetRecoveryCodes ¶ added in v1.9.0
func (s *EncryptedStore) GetRecoveryCodes(ctx context.Context, userID id.UserID) ([]*RecoveryCode, error)
func (*EncryptedStore) ListEnrollments ¶ added in v1.9.0
func (s *EncryptedStore) ListEnrollments(ctx context.Context, userID id.UserID) ([]*Enrollment, error)
func (*EncryptedStore) UpdateEnrollment ¶ added in v1.9.0
func (s *EncryptedStore) UpdateEnrollment(ctx context.Context, e *Enrollment) error
type EnrollRequest ¶ added in v1.9.0
type EnrollRequest struct {
Method string `json:"method,omitempty"` // "totp" or "sms"
Phone string `json:"phone,omitempty"` // Required for SMS method
// Code proves the factor the user already holds when enrolling a
// further one: a current TOTP code or a recovery code.
Code string `json:"code,omitempty"`
}
EnrollRequest is the request body for MFA enrollment.
type EnrollResponse ¶ added in v1.9.0
type EnrollResponse struct {
ID string `json:"id"`
Method string `json:"method"`
Secret string `json:"secret"`
OTPAuthURL string `json:"otpauth_url"`
}
EnrollResponse is returned when MFA enrollment starts.
type Enrollment ¶ added in v1.9.0
type Enrollment struct {
ID id.MFAID `json:"id"`
UserID id.UserID `json:"user_id"`
Method string `json:"method"` // "totp"
Secret string `json:"-"` // Base32-encoded TOTP secret (hidden from JSON)
Verified bool `json:"verified"` // Set true after first successful verification
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Enrollment represents a user's MFA enrollment for a specific method.
type MemoryStore ¶ added in v1.9.0
type MemoryStore struct {
// contains filtered or unexported fields
}
MemoryStore is an in-memory Store for testing.
func NewMemoryStore ¶ added in v1.9.0
func NewMemoryStore() *MemoryStore
NewMemoryStore creates a new in-memory MFA enrollment store.
func (*MemoryStore) ConsumeRecoveryCode ¶ added in v1.9.0
func (s *MemoryStore) ConsumeRecoveryCode(_ context.Context, codeID id.RecoveryCodeID) error
ConsumeRecoveryCode marks a recovery code as used.
func (*MemoryStore) CreateEnrollment ¶ added in v1.9.0
func (s *MemoryStore) CreateEnrollment(_ context.Context, e *Enrollment) error
CreateEnrollment stores a new MFA enrollment.
func (*MemoryStore) CreateRecoveryCodes ¶ added in v1.9.0
func (s *MemoryStore) CreateRecoveryCodes(_ context.Context, codes []*RecoveryCode) error
CreateRecoveryCodes stores a batch of recovery codes.
func (*MemoryStore) DeleteEnrollment ¶ added in v1.9.0
DeleteEnrollment removes an enrollment by ID.
func (*MemoryStore) DeleteRecoveryCodes ¶ added in v1.9.0
DeleteRecoveryCodes removes all recovery codes for a user.
func (*MemoryStore) GetEnrollment ¶ added in v1.9.0
func (s *MemoryStore) GetEnrollment(_ context.Context, userID id.UserID, method string) (*Enrollment, error)
GetEnrollment finds an enrollment by user ID and method.
func (*MemoryStore) GetEnrollmentByID ¶ added in v1.9.0
func (s *MemoryStore) GetEnrollmentByID(_ context.Context, mfaID id.MFAID) (*Enrollment, error)
GetEnrollmentByID finds an enrollment by its ID.
func (*MemoryStore) GetRecoveryCodes ¶ added in v1.9.0
func (s *MemoryStore) GetRecoveryCodes(_ context.Context, userID id.UserID) ([]*RecoveryCode, error)
GetRecoveryCodes returns all recovery codes for a user.
func (*MemoryStore) ListEnrollments ¶ added in v1.9.0
func (s *MemoryStore) ListEnrollments(_ context.Context, userID id.UserID) ([]*Enrollment, error)
ListEnrollments returns all enrollments for a user.
func (*MemoryStore) UpdateEnrollment ¶ added in v1.9.0
func (s *MemoryStore) UpdateEnrollment(_ context.Context, e *Enrollment) error
UpdateEnrollment updates an existing enrollment.
type MongoStore ¶ added in v1.9.0
type MongoStore struct {
// contains filtered or unexported fields
}
MongoStore implements mfa.Store using the Grove MongoDB driver.
func NewMongoStore ¶ added in v1.9.0
func NewMongoStore(db *grove.DB) *MongoStore
NewMongoStore creates a new MongoDB-backed MFA store.
func (*MongoStore) ConsumeRecoveryCode ¶ added in v1.9.0
func (s *MongoStore) ConsumeRecoveryCode(ctx context.Context, codeID id.RecoveryCodeID) error
func (*MongoStore) CreateEnrollment ¶ added in v1.9.0
func (s *MongoStore) CreateEnrollment(ctx context.Context, e *Enrollment) error
func (*MongoStore) CreateRecoveryCodes ¶ added in v1.9.0
func (s *MongoStore) CreateRecoveryCodes(ctx context.Context, codes []*RecoveryCode) error
func (*MongoStore) DeleteEnrollment ¶ added in v1.9.0
func (*MongoStore) DeleteRecoveryCodes ¶ added in v1.9.0
func (*MongoStore) GetEnrollment ¶ added in v1.9.0
func (s *MongoStore) GetEnrollment(ctx context.Context, userID id.UserID, method string) (*Enrollment, error)
func (*MongoStore) GetEnrollmentByID ¶ added in v1.9.0
func (s *MongoStore) GetEnrollmentByID(ctx context.Context, mfaID id.MFAID) (*Enrollment, error)
func (*MongoStore) GetRecoveryCodes ¶ added in v1.9.0
func (s *MongoStore) GetRecoveryCodes(ctx context.Context, userID id.UserID) ([]*RecoveryCode, error)
func (*MongoStore) ListEnrollments ¶ added in v1.9.0
func (s *MongoStore) ListEnrollments(ctx context.Context, userID id.UserID) ([]*Enrollment, error)
func (*MongoStore) UpdateEnrollment ¶ added in v1.9.0
func (s *MongoStore) UpdateEnrollment(ctx context.Context, e *Enrollment) error
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin is the MFA authentication plugin.
func (*Plugin) DeclareSettings ¶ added in v1.9.0
DeclareSettings implements plugin.SettingsProvider.
func (*Plugin) HasMFA ¶ added in v1.9.0
HasMFA returns true if the user has a verified MFA enrollment.
func (*Plugin) MigrationGroups ¶ added in v1.9.0
MigrationGroups implements plugin.MigrationProvider.
func (*Plugin) OnBeforeUserDelete ¶ added in v1.9.0
OnBeforeUserDelete removes a deleted user's MFA enrollments and recovery codes: a second factor for an account that no longer exists is a secret with no owner.
func (*Plugin) OnInit ¶ added in v1.9.0
OnInit is called during engine initialization. It auto-discovers the SMS sender, chronicle, relay, hooks, logger, and ceremony store from the engine.
func (*Plugin) RegisterContract ¶ added in v1.9.0
func (p *Plugin) RegisterContract( d *dispatcher.Dispatcher, reg contract.Registry, wreg contract.WardenRegistry, engine plugin.Engine, ) error
func (*Plugin) RegisterRoutes ¶
RegisterRoutes registers MFA HTTP endpoints on a forge.Router.
func (*Plugin) SetSMSSender ¶ added in v1.9.0
SetSMSSender sets the SMS sender for testing.
type PostgresStore ¶ added in v1.9.0
type PostgresStore struct {
// contains filtered or unexported fields
}
PostgresStore implements mfa.Store using the Grove ORM with PostgreSQL.
func NewPostgresStore ¶ added in v1.9.0
func NewPostgresStore(db *grove.DB) *PostgresStore
NewPostgresStore creates a new PostgreSQL-backed MFA store.
func (*PostgresStore) ConsumeRecoveryCode ¶ added in v1.9.0
func (s *PostgresStore) ConsumeRecoveryCode(ctx context.Context, codeID id.RecoveryCodeID) error
func (*PostgresStore) CreateEnrollment ¶ added in v1.9.0
func (s *PostgresStore) CreateEnrollment(ctx context.Context, e *Enrollment) error
func (*PostgresStore) CreateRecoveryCodes ¶ added in v1.9.0
func (s *PostgresStore) CreateRecoveryCodes(ctx context.Context, codes []*RecoveryCode) error
func (*PostgresStore) DeleteEnrollment ¶ added in v1.9.0
func (*PostgresStore) DeleteRecoveryCodes ¶ added in v1.9.0
func (*PostgresStore) GetEnrollment ¶ added in v1.9.0
func (s *PostgresStore) GetEnrollment(ctx context.Context, userID id.UserID, method string) (*Enrollment, error)
func (*PostgresStore) GetEnrollmentByID ¶ added in v1.9.0
func (s *PostgresStore) GetEnrollmentByID(ctx context.Context, mfaID id.MFAID) (*Enrollment, error)
func (*PostgresStore) GetRecoveryCodes ¶ added in v1.9.0
func (s *PostgresStore) GetRecoveryCodes(ctx context.Context, userID id.UserID) ([]*RecoveryCode, error)
func (*PostgresStore) ListEnrollments ¶ added in v1.9.0
func (s *PostgresStore) ListEnrollments(ctx context.Context, userID id.UserID) ([]*Enrollment, error)
func (*PostgresStore) UpdateEnrollment ¶ added in v1.9.0
func (s *PostgresStore) UpdateEnrollment(ctx context.Context, e *Enrollment) error
type RecoveryCode ¶ added in v1.9.0
type RecoveryCode struct {
ID id.RecoveryCodeID `json:"id"`
UserID id.UserID `json:"user_id"`
CodeHash string `json:"-"` // bcrypt hash of the plaintext code
Used bool `json:"used"` // true after one-time use
UsedAt *time.Time `json:"used_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
RecoveryCode represents a single MFA recovery (backup) code.
func GenerateRecoveryCodes ¶ added in v1.9.0
GenerateRecoveryCodes creates a set of plaintext recovery codes and their bcrypt-hashed RecoveryCode records. Returns both the records (for storage) and the plaintext codes (to show the user once).
type RecoveryRegenerateRequest ¶ added in v1.9.0
type RecoveryRegenerateRequest struct{}
RecoveryRegenerateRequest is an empty request for regenerating recovery codes.
type RecoveryRegenerateResponse ¶ added in v1.9.0
type RecoveryRegenerateResponse struct {
Codes []string `json:"codes"`
}
RecoveryRegenerateResponse is returned with new recovery codes.
type RecoveryVerifyRequest ¶ added in v1.9.0
type RecoveryVerifyRequest struct {
Code string `json:"code"`
}
RecoveryVerifyRequest is the request body for verifying an MFA recovery code.
type RecoveryVerifyResponse ¶ added in v1.9.0
type RecoveryVerifyResponse struct {
ChallengePassed bool `json:"challenge_passed"`
CodesRemaining int `json:"codes_remaining"`
}
RecoveryVerifyResponse is returned on successful recovery code verification.
type SMSChallenge ¶ added in v1.9.0
SMSChallenge represents a pending SMS verification challenge.
func SendSMSChallenge ¶ added in v1.9.0
func SendSMSChallenge(ctx context.Context, sender bridge.SMSSender, phone string) (*SMSChallenge, error)
SendSMSChallenge generates a code and sends it via the SMS bridge.
type SMSSendRequest ¶ added in v1.9.0
type SMSSendRequest struct {
Phone string `json:"phone,omitempty"` // Optional: override the enrolled phone
}
SMSSendRequest is the request body for sending an SMS verification code.
type SMSSendResponse ¶ added in v1.9.0
type SMSSendResponse struct {
Sent bool `json:"sent"`
ExpiresIn int `json:"expires_in_seconds"`
Phone string `json:"phone_masked"`
}
SMSSendResponse is returned when an SMS code is sent.
type SMSVerifyRequest ¶ added in v1.9.0
type SMSVerifyRequest struct {
Code string `json:"code"`
}
SMSVerifyRequest is the request body for verifying an SMS code.
type SMSVerifyResponse ¶ added in v1.9.0
SMSVerifyResponse is returned on successful SMS code verification.
type SqliteStore ¶ added in v1.9.0
type SqliteStore struct {
// contains filtered or unexported fields
}
SqliteStore implements mfa.Store using the Grove ORM with SQLite.
func NewSqliteStore ¶ added in v1.9.0
func NewSqliteStore(db *grove.DB) *SqliteStore
NewSqliteStore creates a new SQLite-backed MFA store.
func (*SqliteStore) ConsumeRecoveryCode ¶ added in v1.9.0
func (s *SqliteStore) ConsumeRecoveryCode(ctx context.Context, codeID id.RecoveryCodeID) error
func (*SqliteStore) CreateEnrollment ¶ added in v1.9.0
func (s *SqliteStore) CreateEnrollment(ctx context.Context, e *Enrollment) error
func (*SqliteStore) CreateRecoveryCodes ¶ added in v1.9.0
func (s *SqliteStore) CreateRecoveryCodes(ctx context.Context, codes []*RecoveryCode) error
func (*SqliteStore) DeleteEnrollment ¶ added in v1.9.0
func (*SqliteStore) DeleteRecoveryCodes ¶ added in v1.9.0
func (*SqliteStore) GetEnrollment ¶ added in v1.9.0
func (s *SqliteStore) GetEnrollment(ctx context.Context, userID id.UserID, method string) (*Enrollment, error)
func (*SqliteStore) GetEnrollmentByID ¶ added in v1.9.0
func (s *SqliteStore) GetEnrollmentByID(ctx context.Context, mfaID id.MFAID) (*Enrollment, error)
func (*SqliteStore) GetRecoveryCodes ¶ added in v1.9.0
func (s *SqliteStore) GetRecoveryCodes(ctx context.Context, userID id.UserID) ([]*RecoveryCode, error)
func (*SqliteStore) ListEnrollments ¶ added in v1.9.0
func (s *SqliteStore) ListEnrollments(ctx context.Context, userID id.UserID) ([]*Enrollment, error)
func (*SqliteStore) UpdateEnrollment ¶ added in v1.9.0
func (s *SqliteStore) UpdateEnrollment(ctx context.Context, e *Enrollment) error
type Store ¶ added in v1.9.0
type Store interface {
// Enrollment CRUD
CreateEnrollment(ctx context.Context, e *Enrollment) error
GetEnrollment(ctx context.Context, userID id.UserID, method string) (*Enrollment, error)
GetEnrollmentByID(ctx context.Context, mfaID id.MFAID) (*Enrollment, error)
UpdateEnrollment(ctx context.Context, e *Enrollment) error
DeleteEnrollment(ctx context.Context, mfaID id.MFAID) error
ListEnrollments(ctx context.Context, userID id.UserID) ([]*Enrollment, error)
// Recovery codes
CreateRecoveryCodes(ctx context.Context, codes []*RecoveryCode) error
GetRecoveryCodes(ctx context.Context, userID id.UserID) ([]*RecoveryCode, error)
ConsumeRecoveryCode(ctx context.Context, codeID id.RecoveryCodeID) error
DeleteRecoveryCodes(ctx context.Context, userID id.UserID) error
}
Store persists MFA enrollment data and recovery codes.
type TOTPConfig ¶
type TOTPConfig struct {
// Issuer is the name of the application (shown in authenticator apps).
Issuer string
// AccountName is the user's identifier (usually email).
AccountName string
}
TOTPConfig configures TOTP generation.
type VerifyMFARequest ¶ added in v1.9.0
type VerifyMFARequest struct {
Code string `json:"code"`
}
VerifyMFARequest is the request body for MFA verification.
type VerifyMFAResponse ¶ added in v1.9.0
type VerifyMFAResponse struct {
Verified bool `json:"verified"`
Method string `json:"method"`
RecoveryCodes []string `json:"recovery_codes,omitempty"`
}
VerifyMFAResponse is returned on successful MFA verification.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package contract wires the mfa plugin's deep-link settings page into the forge-dashboard contract registry.
|
Package contract wires the mfa plugin's deep-link settings page into the forge-dashboard contract registry. |
|
Package mfatest provides a backend-agnostic conformance suite for the mfa.Store interface.
|
Package mfatest provides a backend-agnostic conformance suite for the mfa.Store interface. |