# AshAuthCode
Code-based authentication strategy for [Ash Authentication](https://hexdocs.pm/ash_authentication).
Instead of magic links (clicking a URL), users receive a short numeric code via email or SMS and enter it to authenticate.
## Installation
Add to your dependencies:
```elixir
def deps do
[
{:ash_auth_code, "~> 0.1.0"}
]
end
```
## Usage
Add the extension to your user resource:
```elixir
defmodule MyApp.Accounts.User do
use Ash.Resource,
extensions: [AshAuthentication, AshAuthCode],
domain: MyApp.Accounts
authentication do
tokens do
enabled? true
token_resource MyApp.Accounts.Token
signing_secret fn _, _ -> Application.fetch_env!(:my_app, :token_signing_secret) end
end
strategies do
auth_code do
identity_field :email
code_length 6
token_lifetime {10, :minutes}
registration_enabled? true
sender fn email, code, _opts ->
MyApp.Emails.send_auth_code(email, code)
end
end
end
end
attributes do
uuid_primary_key :id
attribute :email, :ci_string, allow_nil?: false
end
identities do
identity :unique_email, [:email]
end
end
```
## How It Works
1. **Request Phase**: User submits their email
- Strategy generates a JWT token
- Derives a short numeric code from the token (e.g., `847291`)
- Calls your sender with the **code** (not the token)
- Returns the token for server-side storage (e.g., in a cookie)
2. **Verify Phase**: User enters the code they received
- Strategy receives both the token (from cookie) and code (from user)
- Verifies the code matches the token
- Signs in or registers the user
## Configuration Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `identity_field` | atom | `:email` | Field that uniquely identifies the user |
| `code_length` | integer | `6` | Length of the numeric code (4-10) |
| `token_lifetime` | integer or tuple | `{10, :minutes}` | How long the token is valid |
| `registration_enabled?` | boolean | `false` | Allow new user registration |
| `single_use_token?` | boolean | `true` | Revoke token after use |
| `sender` | function | required | Function to send the code |
## Sender Function
The sender receives:
- `identity` - The email/username (string for new users, user struct for existing)
- `code` - The derived numeric code (NOT the token)
- `opts` - Options including `:tenant`
```elixir
sender fn identity, code, opts ->
email = if is_binary(identity), do: identity, else: identity.email
MyApp.Emails.send_auth_code(email, code)
end
```
## HTTP Endpoints
The strategy creates two endpoints:
- `POST /user/auth_code/request` - Request a code
- `POST /user/auth_code/verify` - Verify code and sign in
## License
MIT