AshAuthentication.Strategy.RecoveryCode

Copy Markdown View Source

Strategy for recovery code authentication.

Allows users to authenticate using one-time recovery codes when they can't access their primary authentication method (e.g., TOTP authenticator app). Recovery codes are single-use and deleted after successful verification.

Requirements

  1. A separate Ash resource for storing recovery codes with:
    • A sensitive string attribute for the hashed code
    • A belongs_to relationship to the user resource
    • create, read, and destroy actions
  2. A has_many relationship on the user resource pointing to recovery codes
  3. A brute force protection strategy (rate limiting, audit log, or custom preparation)

Example

defmodule MyApp.Accounts.RecoveryCode do
  use Ash.Resource,
    data_layer: AshPostgres.DataLayer,
    domain: MyApp.Accounts

  attributes do
    uuid_primary_key :id
    attribute :code, :string, sensitive?: true, allow_nil?: false
  end

  relationships do
    belongs_to :user, MyApp.Accounts.User, allow_nil?: false
  end

  actions do
    defaults [:read, :destroy]
    create :create do
      accept [:code]
      argument :user_id, :uuid, allow_nil?: false
      change manage_relationship(:user_id, :user, type: :append)
    end
  end
end

defmodule MyApp.Accounts.User do
  use Ash.Resource,
    extensions: [AshAuthentication],
    domain: MyApp.Accounts

  relationships do
    has_many :recovery_codes, MyApp.Accounts.RecoveryCode
  end

  authentication do
    strategies do
      recovery_code do
        recovery_code_resource MyApp.Accounts.RecoveryCode
        hash_provider AshAuthentication.BcryptProvider
        brute_force_strategy {:preparation, MyApp.NoopPreparation}
      end
    end
  end
end

Actions

The recovery code strategy generates up to two actions:

  • verify - Verifies a recovery code for a user. On success, deletes the used code and returns the user. On failure, returns nil.
  • generate - When generate_enabled? is true, generates new recovery codes for a user. Deletes any existing codes and returns the plaintext codes.

authentication.strategies.recovery_code

recovery_code name \\ :recovery_code

Adds recovery code authentication for account recovery when TOTP is unavailable.

Arguments

NameTypeDefaultDocs
nameatomUniquely identifies the strategy.

Options

NameTypeDefaultDocs
recovery_code_resourcemoduleThe Ash resource module that stores recovery codes. Must have a code attribute and a belongs_to relationship to the user.
brute_force_strategy:rate_limit | {:audit_log, atom} | {:preparation, module}How you are mitigating brute-force recovery code checks.
recovery_codes_relationship_nameatom:recovery_codesThe name of the has_many relationship on the user resource that points to recovery codes.
code_fieldatom:codeThe name of the attribute on the recovery code resource that stores the hashed code.
user_relationship_nameatom:userThe name of the belongs_to relationship on the recovery code resource that points to the user.
hash_providermoduleAshAuthentication.SHA256ProviderThe hash provider to use for hashing and verifying recovery codes. Defaults to AshAuthentication.SHA256Provider which requires codes with at least 60 bits of entropy. For shorter codes, use AshAuthentication.BcryptProvider or AshAuthentication.Argon2Provider.
code_alphabetString.t"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"The set of characters used when generating recovery codes. Each character in the string must be unique.
recovery_code_countpos_integer10The number of recovery codes to generate.
code_lengthpos_integer12The length of each generated recovery code.
verify_action_nameatomThe name to use for the verify action. Defaults to verify_with_<strategy_name>.
generate_enabled?booleantrueWhether to generate the generate action. Set to false if you want to handle code generation yourself.
generate_action_nameatomThe name to use for the generate action. Defaults to generate_<strategy_name>_codes.
audit_log_windowpos_integer | {pos_integer, :days | :hours | :minutes | :seconds}{5, :minutes}Time window for counting failed attempts when using the {:audit_log, ...} brute force strategy. If no unit is provided, then minutes is assumed. Defaults to 5 minutes.
audit_log_max_failurespos_integer5Maximum allowed failures within the window before blocking when using the {:audit_log, ...} brute force strategy. Defaults to 5.

Introspection

Target: AshAuthentication.Strategy.RecoveryCode