<!--
This file was generated by Spark. Do not edit it by hand.
-->
# AshAuthentication.Strategy.Otp
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
```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
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
```elixir
otp name \\ :otp
```
Strategy for authenticating using a one-time password sent to the user
### Options
| Name | Type | Default | Docs |
|------|------|---------|------|
| [`brute_force_strategy`](#authentication-strategies-otp-brute_force_strategy){: #authentication-strategies-otp-brute_force_strategy .spark-required} | `:rate_limit \| {:audit_log, atom} \| {:preparation, module}` | | How you are mitigating brute-force OTP checks. |
| [`sender`](#authentication-strategies-otp-sender){: #authentication-strategies-otp-sender .spark-required} | `(any, any, any -> any) \| module` | | How to send the OTP code to the user. |
| [`identity_field`](#authentication-strategies-otp-identity_field){: #authentication-strategies-otp-identity_field } | `atom` | `:email` | The name of the attribute which uniquely identifies the user, usually something like `username` or `email_address`. |
| [`otp_lifetime`](#authentication-strategies-otp-otp_lifetime){: #authentication-strategies-otp-otp_lifetime } | `pos_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_length`](#authentication-strategies-otp-otp_length){: #authentication-strategies-otp-otp_length } | `pos_integer` | `6` | The length of the generated OTP code. |
| [`otp_characters`](#authentication-strategies-otp-otp_characters){: #authentication-strategies-otp-otp_characters } | `:unambiguous_uppercase \| :unambiguous_alphanumeric \| :digits_only \| :uppercase_letters_only` | `:unambiguous_uppercase` | The 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_generator`](#authentication-strategies-otp-otp_generator){: #authentication-strategies-otp-otp_generator } | `atom` | | A module that implements `generate/1` and `normalize/1`. Defaults to `AshAuthentication.Strategy.Otp.DefaultGenerator`. |
| [`registration_enabled?`](#authentication-strategies-otp-registration_enabled?){: #authentication-strategies-otp-registration_enabled? } | `boolean` | `false` | Allows 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?`](#authentication-strategies-otp-case_sensitive?){: #authentication-strategies-otp-case_sensitive? } | `boolean` | `false` | Whether OTP codes are matched case-sensitively. When `false` (the default), codes are uppercased before comparison so `"xkptmh"` matches `"XKPTMH"`. |
| [`single_use_token?`](#authentication-strategies-otp-single_use_token?){: #authentication-strategies-otp-single_use_token? } | `boolean` | `true` | Automatically revoke the OTP token once it's been used for sign in. |
| [`request_action_name`](#authentication-strategies-otp-request_action_name){: #authentication-strategies-otp-request_action_name } | `atom` | | The name to use for the request action. Defaults to `request_<strategy_name>`. |
| [`sign_in_action_name`](#authentication-strategies-otp-sign_in_action_name){: #authentication-strategies-otp-sign_in_action_name } | `atom` | | The name to use for the sign in action. Defaults to `sign_in_with_<strategy_name>`. |
| [`lookup_action_name`](#authentication-strategies-otp-lookup_action_name){: #authentication-strategies-otp-lookup_action_name } | `atom` | | The action to use when looking up a user by their identity. Defaults to `get_by_<identity_field>`. |
| [`otp_param_name`](#authentication-strategies-otp-otp_param_name){: #authentication-strategies-otp-otp_param_name } | `atom` | `:otp` | The name of the OTP parameter in the incoming sign-in request. |
| [`audit_log_window`](#authentication-strategies-otp-audit_log_window){: #authentication-strategies-otp-audit_log_window } | `pos_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_failures`](#authentication-strategies-otp-audit_log_max_failures){: #authentication-strategies-otp-audit_log_max_failures } | `pos_integer` | `5` | Maximum allowed failures within the window before blocking when using the `{:audit_log, ...}` brute force strategy. Defaults to 5. |
### Introspection
Target: `AshAuthentication.Strategy.Otp`
<style type="text/css">.spark-required::after { content: "*"; color: red !important; }</style>