Sigra.Account.Deletion (Sigra v1.20.0)

Copy Markdown View Source

Account deletion lifecycle: schedule, cancel, execute.

Implements configurable account deletion with three strategies:

  • :soft_delete - Mark as deleted, preserve data (default)
  • :hard_delete - Cascade delete all Sigra tables, remove user row
  • :anonymize - Replace PII with anonymous values, preserve row

Security Properties

  • Grace period (D-14): configurable delay before finalization (default 14 days)
  • Immediate deactivation (D-13): all sessions/tokens revoked on schedule
  • Pending email change auto-cancelled (D-24)
  • MFA data cleared at finalization only (D-13)
  • Cooldown rate limiting (D-22): 24h after cancellation
  • Telemetry events for audit trail (D-26)

Summary

Functions

Cancel scheduled deletion and reactivate account.

Execute deletion based on configured strategy.

Schedule account deletion with grace period.

Check if deletion is scheduled.

Get deletion status.

Check if a cancellation is within the cooldown period.

Functions

cancel(repo, user, opts)

(since 0.8.0)
@spec cancel(module(), map(), keyword()) :: {:ok, map()} | {:error, :not_scheduled}

Cancel scheduled deletion and reactivate account.

Clears deletion timestamps and original_email. The user must re-authenticate to log in (all sessions were revoked on scheduling).

Options

  • :changeset_fn - (user, attrs -> Ecto.Changeset.t()) for user updates

Returns

  • {:ok, user} on success
  • {:error, :not_scheduled} if no deletion is scheduled

execute(repo, user, opts)

(since 0.8.0)
@spec execute(module(), map(), keyword()) :: {:ok, atom()} | {:error, :not_scheduled}

Execute deletion based on configured strategy.

Called by the Oban worker when the grace period expires, or immediately for zero-grace-period configurations.

Strategies

  • :soft_delete - No additional action (deleted_at already set)
  • :hard_delete - Cascade delete Sigra tables, delete user row
  • :anonymize - Replace email with deleted_{id}@deleted.invalid, clear hashed_password, null optional PII fields

All strategies clear MFA data (TOTP secrets, backup codes) per D-13.

Options

  • :changeset_fn - (user, attrs -> Ecto.Changeset.t()) for user updates
  • :token_query_fn - (user, contexts -> Ecto.Queryable.t()) for token cleanup
  • :config - Config with :deletion section

Returns

  • {:ok, strategy} on success
  • {:error, :not_scheduled} if user is not scheduled for deletion

schedule(repo, user, opts)

(since 0.8.0)
@spec schedule(module(), map(), keyword()) ::
  {:ok, map(), DateTime.t()} | {:error, :already_scheduled}

Schedule account deletion with grace period.

Immediately deactivates the account (revokes sessions/tokens), sets deletion timestamps, and preserves the original email for potential restoration.

Options

  • :changeset_fn - (user, attrs -> Ecto.Changeset.t()) for user updates
  • :session_store - SessionStore for session revocation
  • :token_query_fn - (user, contexts -> Ecto.Queryable.t()) for token cleanup
  • :config - Config with :deletion section (strategy, grace_period_days, etc.)

Returns

  • {:ok, user, scheduled_deletion_at} on success
  • {:error, :already_scheduled} if deletion already scheduled

scheduled?(user)

(since 0.8.0)
@spec scheduled?(map()) :: boolean()

Check if deletion is scheduled.

Returns true when both deleted_at and scheduled_deletion_at are set (account is in grace period).

status(user)

(since 0.8.0)
@spec status(map()) :: {:scheduled, non_neg_integer()} | :not_scheduled | :deleted

Get deletion status.

Returns:

  • {:scheduled, days_remaining} when in grace period
  • :not_scheduled when no deletion is pending
  • :deleted when finalized (deleted_at set but no scheduled_deletion_at)

within_cooldown?(cancelled_at, cooldown_hours)

(since 0.8.0)
@spec within_cooldown?(DateTime.t(), non_neg_integer()) :: boolean()

Check if a cancellation is within the cooldown period.

Used to enforce the 24h cooldown after cancelling deletion (D-22). Prevents abuse of the request/cancel cycle.

Parameters

  • cancelled_at - The DateTime when deletion was cancelled
  • cooldown_hours - Number of hours in the cooldown window