Packages
ash_authentication
5.0.0-rc.11
5.0.0-rc.12
5.0.0-rc.11
5.0.0-rc.10
5.0.0-rc.9
5.0.0-rc.8
5.0.0-rc.7
5.0.0-rc.6
5.0.0-rc.5
5.0.0-rc.4
5.0.0-rc.3
5.0.0-rc.2
5.0.0-rc.1
5.0.0-rc.0
4.14.1
4.14.0
4.13.7
4.13.6
4.13.5
4.13.4
4.13.3
4.13.2
retired
4.13.1
retired
4.13.0
4.12.0
4.11.0
4.10.0
4.9.9
4.9.8
4.9.7
4.9.6
4.9.5
4.9.4
4.9.3
4.9.2
4.9.1
4.9.0
4.8.7
4.8.6
4.8.5
4.8.3
4.8.2
4.8.1
4.8.0
4.7.6
4.7.5
4.7.4
4.7.3
4.7.2
4.7.1
4.7.0
4.6.4
4.6.3
4.6.2
4.6.1
4.6.0
4.5.6
4.5.5
4.5.4
4.5.3
4.5.2
4.5.1
4.5.0
4.4.9
4.4.8
4.4.7
4.4.6
4.4.5
4.4.4
4.4.3
4.4.2
4.4.1
4.4.0
4.3.12
4.3.11
4.3.10
4.3.9
4.3.8
4.3.7
4.3.6
4.3.5
4.3.4
4.3.3
4.3.2
4.3.1
4.3.0
4.2.7
4.2.6
4.2.5
4.2.4
4.2.3
4.2.2
4.2.1
4.2.0
4.1.0
4.0.4
4.0.3
4.0.2
4.0.1
4.0.0
4.0.0-rc.6
4.0.0-rc.5
4.0.0-rc.3
4.0.0-rc.2
4.0.0-rc.1
4.0.0-rc.0
3.12.4
3.12.3
3.12.2
3.12.1
3.12.0
3.11.16
3.11.15
3.11.14
3.11.13
3.11.12
3.11.11
3.11.10
3.11.9
3.11.8
3.11.7
3.11.6
3.11.5
3.11.4
3.11.3
3.11.2
3.11.1
3.11.0
3.10.8
3.10.7
3.10.6
3.10.5
3.10.4
3.10.3
3.10.2
3.10.1
3.10.0
3.9.6
3.9.5
3.9.4
3.9.3
3.9.2
3.9.1
3.9.0
3.8.0
3.7.9
3.7.8
3.7.6
3.7.5
3.7.4
3.7.3
3.7.2
3.7.1
3.7.0
3.6.1
3.6.0
3.5.3
3.5.2
3.5.1
3.5.0
3.3.1
3.3.0
3.2.2
3.2.1
3.2.0
3.1.0
3.0.3
Authentication extension for the Ash Framework.
Current section
Files
Jump to
Current section
Files
lib/ash_authentication/strategies/recovery_code.ex
# SPDX-FileCopyrightText: 2022 Alembic Pty Ltd
#
# SPDX-License-Identifier: MIT
defmodule AshAuthentication.Strategy.RecoveryCode do
@moduledoc """
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
```elixir
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.
"""
defstruct __identifier__: nil,
__spark_metadata__: nil,
audit_log_max_failures: 5,
audit_log_window: {5, :minutes},
brute_force_strategy: nil,
code_alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
code_field: :code,
code_length: 12,
generate_action_name: nil,
generate_enabled?: true,
hash_provider: AshAuthentication.SHA256Provider,
name: :recovery_code,
provider: :recovery_code,
recovery_code_count: 10,
recovery_code_resource: nil,
recovery_codes_relationship_name: :recovery_codes,
resource: nil,
user_relationship_name: :user,
verify_action_name: nil
use AshAuthentication.Strategy.Custom, entity: __MODULE__.Dsl.dsl()
@type t :: %__MODULE__{
__identifier__: any,
__spark_metadata__: any,
audit_log_max_failures: pos_integer,
audit_log_window: pos_integer | {pos_integer, :days | :hours | :minutes | :seconds},
brute_force_strategy: :rate_limit | {:audit_log, atom} | {:preparation, module},
code_alphabet: String.t(),
code_field: atom,
code_length: pos_integer,
generate_action_name: atom | nil,
generate_enabled?: boolean,
hash_provider: module,
name: atom,
provider: :recovery_code,
recovery_code_count: pos_integer,
recovery_code_resource: module | nil,
recovery_codes_relationship_name: atom,
resource: Ash.Resource.t() | nil,
user_relationship_name: atom,
verify_action_name: atom | nil
}
defdelegate transform(strategy, dsl), to: __MODULE__.Transformer
defdelegate verify(strategy, dsl), to: __MODULE__.Verifier
end