Packages
ash_authentication
5.0.0-rc.1
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.
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/ash_authentication/strategies/totp.ex
# SPDX-FileCopyrightText: 2022 Alembic Pty Ltd
#
# SPDX-License-Identifier: MIT
defmodule AshAuthentication.Strategy.Totp do
@moduledoc """
Strategy for Time-based One-Time Password (TOTP) authentication.
Provides TOTP support via [nimble_totp](https://hex.pm/packages/nimble_totp),
allowing users to authenticate using time-based codes from authenticator apps
like Google Authenticator, Authy, or 1Password.
## Requirements
Your resource needs to meet the following minimum requirements:
1. Have a primary key.
2. An identity field (e.g., `email` or `username`) for identifying users.
3. A sensitive binary field for storing the TOTP secret.
4. A sensitive datetime field for tracking the last successful TOTP authentication.
5. A brute force protection strategy (rate limiting, audit log, or custom preparation).
## Example
```elixir
defmodule MyApp.Accounts.User do
use Ash.Resource,
extensions: [AshAuthentication],
domain: MyApp.Accounts
attributes do
uuid_primary_key :id
attribute :email, :ci_string, allow_nil?: false, public?: true
attribute :totp_secret, :binary, sensitive?: true
attribute :last_totp_at, :utc_datetime, sensitive?: true
end
authentication do
tokens do
enabled? true
token_resource MyApp.Accounts.Token
end
strategies do
totp do
identity_field :email
issuer "MyApp"
brute_force_strategy {:audit_log, :my_audit_log}
end
end
add_ons do
audit_log :my_audit_log do
audit_log_resource MyApp.Accounts.AuditLog
log_actions [:sign_in_with_totp, :verify_with_totp, :confirm_setup_with_totp]
end
end
end
identities do
identity :unique_email, [:email]
end
end
```
## Actions
The TOTP strategy can generate up to four actions:
- **setup** - Generates a new TOTP secret for the user. Returns the user with
a `totp_url` calculation that can be rendered as a QR code.
- **confirm_setup** - When `confirm_setup_enabled?` is true, this action verifies
a TOTP code before activating the secret. Requires tokens to be enabled.
- **sign_in** - Authenticates a user using their identity and a TOTP code.
- **verify** - Checks if a TOTP code is valid for a given user (without signing in).
## Brute Force Protection
TOTP codes have a small keyspace (typically 6 digits), making them vulnerable
to brute force attacks. You must configure a `brute_force_strategy`:
- `:rate_limit` - Uses `AshRateLimiter` to limit attempts.
- `{:audit_log, :audit_log_name}` - Uses an audit log to track failed attempts.
- `{:preparation, ModuleName}` - Custom preparation for rate limiting.
## Working with Actions
You can interact with TOTP actions via the `AshAuthentication.Strategy` protocol:
iex> strategy = AshAuthentication.Info.strategy!(MyApp.Accounts.User, :totp)
...> {:ok, user} = AshAuthentication.Strategy.action(strategy, :setup, %{user: existing_user})
...> user.totp_url_for_totp # QR code URL
iex> {:ok, true} = AshAuthentication.Strategy.action(strategy, :verify, %{user: user, code: "123456"})
"""
defstruct __identifier__: nil,
__spark_metadata__: nil,
audit_log_max_failures: 5,
audit_log_window: {5, :minutes},
brute_force_strategy: nil,
confirm_setup_enabled?: false,
confirm_setup_action_name: nil,
grace_period: nil,
identity_field: nil,
issuer: nil,
last_totp_at_field: nil,
name: :totp,
period: 30,
read_secret_from: nil,
resource: nil,
secret_field: nil,
secret_length: 20,
setup_enabled?: true,
setup_action_name: nil,
setup_token_lifetime: {10, :minutes},
sign_in_enabled?: false,
sign_in_action_name: nil,
totp_url_field: nil,
verify_enabled?: true,
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},
confirm_setup_enabled?: boolean,
confirm_setup_action_name: atom,
grace_period: non_neg_integer | nil,
identity_field: atom,
issuer: String.t(),
last_totp_at_field: atom,
name: atom,
period: pos_integer,
read_secret_from: atom | nil,
resource: Ash.Resource.t(),
secret_field: atom,
secret_length: pos_integer,
setup_enabled?: boolean,
setup_action_name: atom,
setup_token_lifetime: pos_integer | {pos_integer, :days | :hours | :minutes | :seconds},
sign_in_enabled?: boolean,
sign_in_action_name: atom,
totp_url_field: atom,
verify_enabled?: boolean,
verify_action_name: atom
}
defdelegate transform(strategy, dsl), to: __MODULE__.Transformer
defdelegate verify(strategy, dsl), to: __MODULE__.Verifier
end