<!--
SPDX-FileCopyrightText: 2022 Alembic Pty Ltd
SPDX-License-Identifier: MIT
-->
# Upgrading
## Upgrading to version 5.0.0
Version 5.0.0 includes several breaking changes related to action types and the Google OAuth strategy. Most changes can be handled automatically using the upgrade task.
### Dependencies
This version requires Assent ~> 0.3.0 (updated from ~> 0.2.9) and adds `nimble_totp` as a dependency for the new TOTP strategy. Run `mix deps.get` after updating your `ash_authentication` version.
### Automated Upgrade
If you have [Igniter](https://hexdocs.pm/igniter) installed, run:
```bash
mix igniter.upgrade ash_authentication 4.x.x 5.0.0
```
Replace `4.x.x` with your current version. This will automatically:
- Convert token resource `revoked?` actions from `:read` to `:action` type
- Convert password reset and magic link request actions from `:read` to `:action` type
- Create `get_by_<identity_field>` read actions where needed
- Update `"google_hd"` references to `"hd"` in your codebase
- Add identity-keyed brute-force protection to password and magic link strategies, generating an `audit_log` add-on (and audit log resource) if one is not already present
### Breaking Changes
#### 1. Sender failures now propagate as errors
Previously, sender failures were silently ignored. Now, senders that return `{:error, reason}` will cause the authentication action to fail with an `AshAuthentication.Errors.SenderFailed` error.
**What this affects:**
- Password reset requests
- Magic link requests
- Confirmation emails
**Action required:** Review your sender implementations. If they can return `{:error, reason}`, ensure your application handles these failures appropriately. Senders returning `:ok` or `{:ok, result}` (common with mailer libraries) will continue to work unchanged.
**Recommended approach:** Consider using a durable background job library like [Oban](https://hexdocs.pm/oban) for sending authentication emails. This provides automatic retries, failure tracking, and prevents transient email delivery issues from blocking user authentication flows. Your sender can enqueue a job and return `:ok` immediately, while the actual email delivery happens asynchronously with built-in resilience.
```elixir
defmodule MyApp.AuthEmailSender do
use AshAuthentication.Sender
def send(user_or_email, token, opts) do
%{user_or_email: user_or_email, token: token, opts: opts}
|> MyApp.Workers.AuthEmail.new()
|> Oban.insert()
:ok
end
end
```
#### 2. Request actions converted to generic actions
Password reset request (`request_password_reset_with_password`) and magic link request (`request_magic_link`) actions are now generated as `:action` type instead of `:read`.
**Action required:** If you have customised these actions as `:read` actions, the upgrader will convert them automatically. If you've made extensive customisations, review the converted code to ensure it still meets your requirements.
The new actions work with auto-generated `get_by_<identity_field>` read actions for user lookup.
#### 3. Token revoked action converted to generic action
The `revoked?` action on token resources is now a generic action returning a boolean, rather than a read action returning a record.
**Action required:** If you have a custom `revoked?` read action on your token resource, the upgrader will convert it automatically.
#### 4. Google strategy now uses OIDC
The Google OAuth strategy now uses OIDC (via Assent 0.3.0) instead of the legacy API. This changes two fields in the `user_info` map:
| Old | New |
|-----|-----|
| `user_info["google_hd"]` | `user_info["hd"]` |
| `user_info["email_verified"]` (string `"true"`) | `user_info["email_verified"]` (boolean `true`) |
**Action required:**
1. The upgrader will automatically rename `"google_hd"` to `"hd"` in your code
2. You must manually update any checks for `email_verified`:
```elixir
# Before
user_info["email_verified"] == "true"
# After
user_info["email_verified"] == true
```
### Igniter Task Changes
#### Phoenix-specific code moved to ash_authentication_phoenix
The igniter tasks for password, magic_link, TOTP, and confirmation strategies no longer generate Phoenix-specific code (Swoosh senders with verified routes, controller modifications). AA's tasks now generate only resource-level code with basic `IO.puts` senders.
If you use `ash_authentication_phoenix`, its new convention-named strategy tasks (`ash_authentication_phoenix.add_strategy.password`, etc.) handle the Phoenix integration automatically — upgrading senders to use Swoosh and verified routes, modifying the AuthController for TOTP 2FA, and inserting TOTP routes.
**Action required:** If you re-run the igniter installer, the Phoenix-specific sender code will now come from AAP's tasks. Existing senders in your project are not affected — the igniter uses `on_exists: :warning` for sender modules, so existing customisations are preserved.
### New Features
#### TOTP Two-Factor Authentication
Version 5.0.0 adds a complete TOTP (Time-based One-Time Password) strategy for two-factor authentication. See the [TOTP tutorial](/documentation/tutorials/totp.md) for setup instructions.
To add TOTP to an existing project with `ash_authentication_phoenix`:
```bash
mix ash_authentication_phoenix.add_strategy totp
```
This composes both the AA resource-level task and the AAP Phoenix integration task.
#### Extra JWT Claims
You can now add custom claims to JWT tokens using the `extra_claims` option in the tokens DSL section, or dynamically via `AshAuthentication.add_token_claims/2`. See the [tokens guide](/documentation/topics/tokens.md) for details.
#### Identity-keyed brute-force protection
The `password` and `magic_link` strategies now support a `brute_force_strategy` option that counts recent failures by identity (via the audit log add-on) and blocks further attempts when the configured threshold is reached. The upgrader enables this automatically by:
- Ensuring an `audit_log` add-on (and audit log resource) is present on each resource using `password` or `magic_link`.
- Adding `brute_force_strategy {:audit_log, <name>}` to each affected strategy block.
Defaults to a 5 minute window with 5 allowed failures; tune `audit_log_window` and `audit_log_max_failures` on each strategy if you need different thresholds.
#### Sign in with Apple and other `response_mode=form_post` providers
OAuth2/OIDC callbacks now accept **`POST` as well as `GET`**. This supports providers that use `response_mode=form_post` — most importantly **Sign in with Apple**, which returns the callback as a `POST` whenever `name` or `email` scope is requested.
That callback is a *cross-site* `POST` from the provider, so the browser does not send the `SameSite=Lax` session cookie with it — which is why the stored `state`/`nonce`/PKCE verifier couldn't be read and sign-in failed. To handle this **without** weakening your session cookie to `SameSite=None`, the callback now renders a small **"signing you in…" interstitial** that immediately re-`POST`s the parameters *same-origin* (kept in the request body, never the URL), where the session cookie *is* sent and sign-in completes. The page fails closed if it can't recover the session, and is a no-op for providers that use a normal `GET` callback.
Because the callback is a cross-site `POST`, Phoenix's `:protect_from_forgery` (which has no CSRF token to check) would otherwise reject it — so it must be skipped for that request (OAuth `state` is the CSRF defence). `ash_authentication_phoenix` provides a `:skip_csrf_for_oauth_callback` plug that skips CSRF **only** for `POST`s to callback paths; every other request (including all other auth POSTs) stays fully protected.
**Using `ash_authentication_phoenix`:** when your resources define an OAuth/OIDC strategy, the installer and upgrader do two things automatically:
1. Generate an editable interstitial — a `signing_in.html.eex` template, an `AuthInterstitialHTML` renderer module, and the `:oauth2_interstitial_renderer` config. Edit `signing_in.html.eex` to customise copy/branding.
2. Prepend `plug :skip_csrf_for_oauth_callback` to your `:browser` pipeline (before `:protect_from_forgery`).
Both happen when you add a provider via `mix ash_authentication_phoenix.add_strategy apple` (or `google`, `github`, `oidc`, …), and the upgrader applies them to existing OAuth apps.
**Hand-rolled routers / non-Phoenix apps:** no code change is required to *route* the callback (it already accepts both methods), and the built-in default interstitial renders with zero configuration. Keep your session cookie on the default `SameSite=Lax`, but:
- Ensure the callback `POST` is exempt from CSRF. With `ash_authentication_phoenix`, add its plug before `:protect_from_forgery`:
```elixir
pipeline :browser do
# ...
plug :skip_csrf_for_oauth_callback
plug :protect_from_forgery
end
```
- To customise the interstitial page, set:
```elixir
config :ash_authentication, :oauth2_interstitial_renderer, {MyApp.SomeModule, :render}
```
where the function takes the render assigns (`:action`, `:params`, `:reflected_param`) and returns the HTML.
### Other Improvements
- **Auto signout in AshAuthentication.Phoenix** - Automatic sign-out is now supported in the Phoenix integration
- **API key header prefix regex support** - The `ApiKey.Plug` now accepts regex patterns for header prefix matching
- **Better error handling** - `Jwt.token_for_user/2` now returns `{:error, AuthenticationFailed.t}` on failure instead of raising
## Upgrading to version 4.0.0
Version 4.0.0 of AshAuthentication adds support for Ash 3.0 and in line with [a number of changes in Ash](`e:ash:upgrading-to-3.0.html`) there are some corresponding changes to Ash Authentication:
- Token generation is enabled by default, meaning that you will have to explicitly set [`authentication.tokens.enabled?`](documentation/dsls/DSL-AshAuthentication.md#authentication-tokens-enabled?) to `false` if you don't need them.
- Sign in tokens are enabled by default in the password strategy. What this means is that instead of returning a regular user token on sign-in in the user's metadata, we generate a short-lived token which can be used to actually sign the user in. This is specifically to allow live-view based sign-in UIs to display an authentication error without requiring a page-load.
## Upgrading to version 3.6.0.
As of version 3.6.0 the `TokenResource` extension adds the `subject` attribute
which allows us to more easily match tokens to specific users. This unlocks
some new use-cases (eg sign out everywhere).
This means that you will need to generate new migrations and migrate your
database.
### Upgrade steps:
> ### Warning {: .warning}
>
> If you already have tokens stored in your database then the migration will
> likely throw a migration error due to the new `NOT NULL` constraint on
> `subject`. If this happens then you can either delete all your tokens or
> explicitly add the `subject` attribute to your resource with `allow_nil?` set
> to `true`. eg:
>
> ```elixir
> attributes do
> attribute :subject, :string, allow_nil?: true
> end
> ```
1. Run `mix ash_postgres.generate_migrations --name=add_subject_to_token_resource`
2. Run `mix ash_postgres.migrate`
3. 🎉