AshAuthentication.Strategy.Totp

Copy Markdown View Source

Strategy for Time-based One-Time Password (TOTP) authentication.

Provides TOTP support via 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

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"})

authentication.strategies.totp

totp name \\ :totp

Adds TOTP-based one-time passcode authentication.

Arguments

NameTypeDefaultDocs
nameatomUniquely identifies the strategy.

Options

NameTypeDefaultDocs
brute_force_strategy:rate_limit | {:audit_log, atom} | {:preparation, module}How you are mitigating brute-force token checks.
identity_fieldatom:usernameThe name of the attribute which uniquely identifies the user, usually something like username or email_address.
issuerString.tThe TOTP issuer to use. Defaults to the strategy name.
secret_fieldatom:totp_secretThe name of the attribute within which to store the TOTP secret.
read_secret_fromatomThe attribute or calculation to read the TOTP secret from. Defaults to secret_field. Useful with AshCloak where encrypted values are read via calculations.
secret_lengthpos_integer20The number of bytes to use when generating secrets. Default is 20 as per the HOTP RFC.
last_totp_at_fieldatom:last_totp_atThe name of the attribute or calculation used to track the last successful TOTP time.
periodpos_integer30The period (in seconds) in which the code is valid.
grace_periodnon_neg_integer | nilThe number of additional previous time periods to accept when validating TOTP codes. For example, 1 also accepts the previous period's code. See the NimbleTOTP grace period docs. Defaults to nil (no grace period).
setup_enabled?booleantrueIf you do not want the setup action to be generated/validated you disable it by setting this to false.
setup_action_nameatomThe name to use for the setup action. Defaults to setup_with_<strategy_name>.
totp_url_fieldatomThe name to use for the TOTP URL calculation. Defaults to totp_url_for_<strategy_name>.
sign_in_enabled?booleanfalseIf you do not want users to be able to sign in using this strategy, set this to false.
sign_in_action_nameatomThe name to use for the sign in action. Defaults to sign_in_with_<strategy_name>.
verify_enabled?booleantrueIf you do not want users to be able to verify their TOTP codes outside of the sign-in action (or you want to handle it yourself), set this to false.
verify_action_nameatomThe name to use for the verify action. Defaults to verify_with_<strategy_name>.
confirm_setup_enabled?booleanfalseWhen enabled, setup generates a token that must be confirmed with a valid TOTP code before the secret is stored.
confirm_setup_action_nameatomThe name to use for the confirm setup action. Defaults to confirm_setup_with_<strategy_name>.
setup_token_lifetimepos_integer | {pos_integer, :days | :hours | :minutes | :seconds}{10, :minutes}How long the setup token is valid. If no unit is provided, then minutes is assumed. Defaults to 10 minutes.
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.Totp