Sigra.MFA.Lockout (Sigra v0.2.2)

Copy Markdown View Source

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_all prevents 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

check(credential, config)

(since 0.6.0)
@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.

increment(repo, mfa_credential_schema, credential_id, config)

(since 0.6.0)
@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 module
  • mfa_credential_schema - The generated MFA credential Ecto schema module
  • credential_id - The credential's database ID
  • config - Sigra config with MFA settings

reset(repo, mfa_credential_schema, credential_id)

(since 0.6.0)
@spec reset(module(), module(), term()) :: :ok

Reset MFA lockout state on a credential.

Sets failed_attempts = 0 and locked_until = nil.