AshAuthentication.Strategy.Otp

Copy Markdown View Source

Strategy for authentication using a one-time password (OTP).

In order to use OTP authentication your resource needs to meet the following minimum requirements:

  1. Have a primary key.
  2. A uniquely constrained identity field (eg username or email)
  3. Have tokens enabled.

There are other options documented in the DSL.

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
  end

  authentication do
    tokens do
      enabled? true
      store_all_tokens? true
      token_resource MyApp.Accounts.Token
      signing_secret MyApp.Secrets
    end

    strategies do
      otp do
        identity_field :email
        brute_force_strategy :rate_limit
        otp_lifetime {10, :minutes}
        otp_length 6
        otp_characters :unambiguous_uppercase
        sender MyApp.OtpSender
      end
    end
  end

  identities do
    identity :unique_email, [:email]
  end
end

Actions

By default the OTP strategy will automatically generate the request and sign-in actions for you, however you're free to define them yourself. If you do, then the action will be validated to ensure that all the needed configuration is present.

If you wish to work with the actions directly from your code you can do so via the AshAuthentication.Strategy protocol.

Examples

Requesting that an OTP code is sent for a user:

iex> strategy = Info.strategy!(Example.UserWithOtp, :otp)
...> Strategy.action(strategy, :request, %{"email" => "user@example.com"})
:ok

Signing in using an OTP code:

iex> strategy = Info.strategy!(Example.UserWithOtp, :otp)
...> {:ok, user} = Strategy.action(strategy, :sign_in, %{"email" => "user@example.com", "otp" => "ABCDEF"})

Plugs

The OTP strategy provides plug endpoints for both request and sign-in actions.

If you wish to work with the plugs directly, you can do so via the AshAuthentication.Strategy protocol.

authentication.strategies.otp

otp name \\ :otp

Strategy for authenticating using a one-time password sent to the user

Options

NameTypeDefaultDocs
brute_force_strategy:rate_limit | {:audit_log, atom} | {:preparation, module}How you are mitigating brute-force OTP checks.
sender(any, any, any -> any) | moduleHow to send the OTP code to the user.
identity_fieldatom:emailThe name of the attribute which uniquely identifies the user, usually something like username or email_address.
otp_lifetimepos_integer | {pos_integer, :days | :hours | :minutes | :seconds}{10, :minutes}How long the OTP code is valid. If no unit is provided, then minutes is assumed.
otp_lengthpos_integer6The length of the generated OTP code.
otp_characters:unambiguous_uppercase | :unambiguous_alphanumeric | :digits_only | :uppercase_letters_only:unambiguous_uppercaseThe character set used to generate OTP codes: - :unambiguous_uppercase (default) — A–Z minus easily misread characters (I, L, O, S, Z) - :unambiguous_alphanumeric — unambiguous letters and digits combined - :digits_only — full 0–9 - :uppercase_letters_only — full A–Z
otp_generatoratomA module that implements generate/1 and normalize/1. Defaults to AshAuthentication.Strategy.Otp.DefaultGenerator.
registration_enabled?booleanfalseAllows registering via OTP. Sign-in becomes an upsert action instead of a read action, so users who don't exist are created on first sign-in.
case_sensitive?booleanfalseWhether OTP codes are matched case-sensitively. When false (the default), codes are uppercased before comparison so "xkptmh" matches "XKPTMH".
single_use_token?booleantrueAutomatically revoke the OTP token once it's been used for sign in.
request_action_nameatomThe name to use for the request action. Defaults to request_<strategy_name>.
sign_in_action_nameatomThe name to use for the sign in action. Defaults to sign_in_with_<strategy_name>.
lookup_action_nameatomThe action to use when looking up a user by their identity. Defaults to get_by_<identity_field>.
otp_param_nameatom:otpThe name of the OTP parameter in the incoming sign-in request.
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.Otp