MFA-specific lockout logic, mirroring Sigra.Lockout pattern.
Checks and manages MFA lockout state based on failed verification attempts on the MFA credential record. After reaching the configured threshold, the MFA credential is temporarily locked for a configurable duration.
Security Properties
- Lockout counter is per-user on MFA credential, not per-session (D-31)
- Atomic increment via
update_allprevents race conditions (Pitfall 4) - Shared counter for TOTP and backup code attempts (D-19)
- Auto-unlocks after duration expires
Summary
Functions
Check if an MFA credential is locked out.
Atomically increment failed MFA attempts on a credential.
Reset MFA lockout state on a credential.
Functions
@spec check(Sigra.MFA.Credential.t(), Sigra.Config.t()) :: :ok | {:error, :lockout, non_neg_integer()}
Check if an MFA credential is locked out.
Returns :ok if the credential is not locked, or
{:error, :lockout, remaining_seconds} if currently locked.
Uses mfa.lockout_threshold and mfa.lockout_duration from config.
@spec increment(module(), module(), term(), Sigra.Config.t()) :: {:ok, %{failed_attempts: non_neg_integer(), locked: boolean()}}
Atomically increment failed MFA attempts on a credential.
If the new count meets or exceeds the threshold, also sets locked_until.
Returns {:ok, %{failed_attempts: n, locked: boolean}}.
Parameters
repo- The Ecto repo modulemfa_credential_schema- The generated MFA credential Ecto schema modulecredential_id- The credential's database IDconfig- Sigra config with MFA settings
Reset MFA lockout state on a credential.
Sets failed_attempts = 0 and locked_until = nil.