Documentation
¶
Index ¶
- Variables
- func GenerateTOTPBackupCodes() ([]string, error)
- func HashTOTPBackupCodes(secret string, codes []string) ([]string, error)
- func LoginSecondFactorVerifier(ctx *gst.ServiceContext, userID string, factor authn.LoginSecondFactor) error
- func SetAccountAdministrator(admin AccountAdministrator)
- type AccountAdministrator
- type AdminTOTPResetService
- type AdminTOTPStatusService
- type TOTPBindService
- type TOTPConfirmService
- type TOTPStatusService
- type TOTPUnbindService
Constants ¶
This section is empty.
Variables ¶
var ErrAccountAdministratorNotConfigured = errors.New("mfa account administrator is not configured")
ErrAccountAdministratorNotConfigured is returned by administrative MFA flows until the host application installs a real AccountAdministrator.
Functions ¶
func GenerateTOTPBackupCodes ¶
GenerateTOTPBackupCodes creates one-time recovery codes for a new TOTP device.
Each code is 16 characters drawn from an unambiguous Base32 alphabet, formatted in groups of four for manual entry. Callers return these raw codes only once to the user during device confirmation.
func HashTOTPBackupCodes ¶
HashTOTPBackupCodes normalizes and hashes recovery codes before storage, keyed by the owning device's TOTP secret.
Stored devices keep only HMAC-SHA256 digests of normalized recovery codes, so database reads never expose usable recovery credentials. A recovery code is an 80-bit high-entropy random string, not a human-chosen password, so a slow password hash would add cost without adding brute-force resistance; HMAC keyed per device with the secret the device already owns needs no new key management and keeps digests independent across devices.
func LoginSecondFactorVerifier ¶
func LoginSecondFactorVerifier(ctx *gst.ServiceContext, userID string, factor authn.LoginSecondFactor) error
LoginSecondFactorVerifier enforces the MFA rules used during IAM login. It is the gate installed through authn.SetLoginSecondFactorVerifier: module/mfa installs it on the add path, and project-owned assembly code installs it on the copy path. Nothing here installs itself, so the gate is never armed by a package import alone.
Accounts without active TOTP devices pass untouched. Enrolled accounts must submit exactly one proof: a TOTP code, consumed against replay on success, or a recovery code, removed transactionally. Each proof first spends one attempt from the user's login budget, and a verified proof resets it. Per the authn contract the verifier owns the client-facing error shape, and clients branch on status plus message: the stable 401 authn.MsgSecondFactorRequired tells a login UI to prompt for the code, 401 with other messages reports an invalid proof, 400 reports both proofs arriving at once, and 429 reports a spent budget, which refuses even a correct proof until its window ends.
func SetAccountAdministrator ¶
func SetAccountAdministrator(admin AccountAdministrator)
SetAccountAdministrator installs the host application's administrative authorizer. Call it during application/module initialization before serving requests. Passing nil restores the safe default that denies every administrative MFA call with ErrAccountAdministratorNotConfigured.
Types ¶
type AccountAdministrator ¶
type AccountAdministrator interface {
// EnsureCanAdminister rejects the request unless the current actor may
// administer the target account's MFA enrollment.
EnsureCanAdminister(ctx *gst.ServiceContext, targetUserID string) error
}
AccountAdministrator authorizes administrative MFA operations on a target account. Implementations decide who may inspect or reset another account's enrollment; the built-in module adapter delegates to the framework IAM tenant-admin rules.
MFA intentionally does not own that authority model. Projects copied with `gg module copy mfa` install their implementation from a project-owned file outside service/mfa; until one is installed every administrative call is denied.
type AdminTOTPResetService ¶
type AdminTOTPResetService struct {
service.Base[*modelmfa.AdminTOTP, *model.Empty, *modelmfa.AdminTOTPResetRsp]
}
AdminTOTPResetService force-clears a target account's TOTP enrollment.
This is the rescue path for an account that lost both its authenticator device and its recovery codes: self-service unbind deliberately accepts no password, so only an administrator can switch MFA off for such an account. The reset hard-deletes every device — active or still staged — so no shared secret or recovery-code hash survives, and the account logs in without a second factor until it enrolls again. It also forgets the account's verification attempt budgets, so a lockout earned while the factor was lost does not follow the account into its new enrollment.
func (*AdminTOTPResetService) Delete ¶
func (a *AdminTOTPResetService) Delete(ctx *gst.ServiceContext, req *model.Empty) (rsp *modelmfa.AdminTOTPResetRsp, err error)
Delete removes every TOTP device of the target user on behalf of an administrator.
type AdminTOTPStatusService ¶
type AdminTOTPStatusService struct {
service.Base[*modelmfa.AdminTOTP, *model.Empty, *modelmfa.AdminTOTPStatusRsp]
}
AdminTOTPStatusService returns a target account's TOTP enrollment state to an administrator. Authorization is delegated to the installed AccountAdministrator before any enrollment data is read, and the response is the same sanitized view the self-service status endpoint renders: device metadata only, never secrets or recovery-code hashes.
func (*AdminTOTPStatusService) Get ¶
func (a *AdminTOTPStatusService) Get(ctx *gst.ServiceContext, req *model.Empty) (rsp *modelmfa.AdminTOTPStatusRsp, err error)
Get loads the target user's TOTP enrollment view for an administrator.
type TOTPBindService ¶
type TOTPBindService struct {
service.Base[*modelmfa.TOTPBind, *model.Empty, *modelmfa.TOTPBindRsp]
}
TOTPBindService starts the TOTP binding flow for an authenticated account.
The service validates the current user and session, generates a new TOTP secret, renders the provisioning URL as a QR code, and stores the secret in a short-lived binding challenge tied to the current user and session. The response returns only the challenge ID and authenticator setup data, never the raw secret as a standalone field.
func (*TOTPBindService) Create ¶
func (t *TOTPBindService) Create(ctx *gst.ServiceContext, req *model.Empty) (rsp *modelmfa.TOTPBindRsp, err error)
Create creates a pending TOTP binding challenge and returns setup metadata.
The method requires an authenticated account and session, generates a new server-held secret, stores it in the binding challenge, and returns the challenge ID with the provisioning URL and QR image for authenticator setup.
type TOTPConfirmService ¶
type TOTPConfirmService struct {
service.Base[*modelmfa.TOTPConfirm, *modelmfa.TOTPConfirmReq, *modelmfa.TOTPConfirmRsp]
}
TOTPConfirmService completes a pending TOTP binding flow.
The service loads the cached binding challenge, ensures it belongs to the current user and session, validates the submitted TOTP code against the server-held secret, consumes the challenge, and then creates the active device. It returns one-time recovery codes only in this response while storing HMAC digests in the device record. Consuming the challenge before any storage work means a failure can never leave an activated device whose recovery codes were generated but never delivered.
func (*TOTPConfirmService) Create ¶
func (t *TOTPConfirmService) Create(ctx *gst.ServiceContext, req *modelmfa.TOTPConfirmReq) (rsp *modelmfa.TOTPConfirmRsp, err error)
Create turns a valid binding challenge into an active TOTP device.
The method verifies challenge ownership, checks and consumes the submitted TOTP code, consumes the challenge, creates recovery codes, stores only their hashes, and persists the device. The (user_id, secret) unique index is the duplicate guard: a concurrent confirm of the same challenge loses on Create with a conflict.
type TOTPStatusService ¶
type TOTPStatusService struct {
service.Base[*modelmfa.TOTPStatus, *model.Empty, *modelmfa.TOTPStatusRsp]
}
TOTPStatusService returns the current authenticated account's TOTP enrollment state. The service keeps the status view scoped to ctx.UserID(), counts only active devices as enabling MFA, and returns device metadata without exposing secrets or recovery-code hashes.
The request type is *model.Empty to match the DSL: a List action that only declares a Result always parses with an empty payload, and the add path must register the same request type the copy path generates.
func (*TOTPStatusService) List ¶
func (t *TOTPStatusService) List(ctx *gst.ServiceContext, req *model.Empty) (rsp *modelmfa.TOTPStatusRsp, err error)
List loads the current user's TOTP devices and builds the status response used by clients to render MFA settings. It requires an authenticated request, returns active devices only, and derives Enabled from the active device count.
type TOTPUnbindService ¶
type TOTPUnbindService struct {
service.Base[*modelmfa.TOTPUnbind, *modelmfa.TOTPUnbindReq, *modelmfa.TOTPUnbindRsp]
}
TOTPUnbindService removes an active TOTP device after fresh authentication.
The request must provide exactly one verification method: a TOTP code or a recovery code, both proving possession of the second factor itself — the password alone deliberately cannot switch MFA off. Verification runs against the current user's active devices; recovery-code removal and target-device deletion share the same transaction so the code is consumed only when the unbind operation succeeds.
Each well-formed request spends one attempt from the user's unbind budget before its proof is checked, and a verified proof resets the budget.
Failures answer through service errors like the rest of the module: 400 for malformed requests, 401 for failed fresh authentication, 404 for a missing target device, 429 once the unbind budget is spent. Credentials are always judged before the target device is looked up, so an unauthenticated caller cannot probe device existence.
func (*TOTPUnbindService) Create ¶
func (t *TOTPUnbindService) Create(ctx *gst.ServiceContext, req *modelmfa.TOTPUnbindReq) (rsp *modelmfa.TOTPUnbindRsp, err error)
Create validates fresh authentication, removes the target device, and returns the remaining active count.