<!--
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. 🎉
