# Getting Started

This guide walks you through adding multi-account linking and switching to an existing Ash + AshAuthentication + Phoenix app. By the end, your users will be able to link multiple accounts to a single browser session and switch between them without re-authenticating.

## Prerequisites

AshMultiAccount adds multi-account support **on top of** an existing Ash + AshAuthentication + Phoenix app. Before installing, you need:

### Required

These are pulled in automatically as transitive dependencies of `ash_multi_account`:

- **Ash and Spark**
- **AshAuthentication** — you must have at least one authentication strategy configured
- **Phoenix** — controllers, plugs, and router all require it

You also need:

- **A user resource** — an Ash resource with AshAuthentication set up and registered in a domain (can be any module name, e.g. `MyApp.Accounts.User`, `MyApp.Accounts.Person`, etc.)
- **AshAuthentication Phoenix** (`ash_authentication_phoenix`) — provides `AshAuthentication.Phoenix.Controller` which is used by the auth controller that handles sign-in callbacks. Most Phoenix + AshAuthentication apps already have this.

> **Don't have a user resource yet?** Follow the [AshAuthentication Getting Started guide](https://hexdocs.pm/ash_authentication/get-started.html) to create one with an authentication strategy, then come back here.

### Also needed for LiveView

If your app uses LiveView for authenticated pages, also add:

- **Phoenix LiveView** (`phoenix_live_view`) — needed for the LiveView hook (`AshMultiAccount.Phoenix.LiveHook`) and the account switcher component (`AshMultiAccount.Phoenix.Components`).

> **Note:** `ash_authentication_phoenix` also provides `AshAuthentication.Phoenix.LiveSession` which the multi-account LiveView hook runs alongside. You likely already have it if your app uses AshAuthentication with LiveView.

The installer patches your existing resources and router; it does not create a User resource, domain, or authentication setup from scratch.

Specific version requirements are defined in the library's `mix.exs` — running `mix deps.get` will ensure compatible versions are resolved automatically.

### Data Layer

AshMultiAccount is **data layer agnostic**. It works with any Ash data layer — AshPostgres, AshSqlite, ETS, or others. The library generates standard Ash resources with attributes, relationships, and actions that work on any data layer. Both the demo app and the library's own test suite use ETS.

### Authentication Strategy

AshMultiAccount is **strategy-agnostic**. It works with any AshAuthentication strategy — password, OAuth2, magic links, API keys, or any combination. The library hooks into the session layer *after* authentication completes, so it doesn't care how the user originally signed in. The demo app uses password authentication for simplicity, but the same setup works with any strategy.

## Installation

Add the dependencies to your `mix.exs`. What you need depends on your setup:

<!-- tabs-open -->

### Using Igniter (recommended)

```elixir
# mix.exs
def deps do
  [
    {:ash_multi_account, "~> 0.1.0"},
    {:igniter, "~> 0.6"},
    # If not already present:
    {:ash_authentication_phoenix, "~> 2.0"},
    # Also add if using LiveView:
    {:phoenix_live_view, "~> 1.0"}
  ]
end
```

### Manual

```elixir
# mix.exs
def deps do
  [
    {:ash_multi_account, "~> 0.1.0"},
    # If not already present:
    {:ash_authentication_phoenix, "~> 2.0"},
    # Also add if using LiveView:
    {:phoenix_live_view, "~> 1.0"}
  ]
end
```

<!-- tabs-close -->

Run `mix deps.get` to fetch the dependencies.

<!-- tabs-open -->

### Using Igniter (recommended)

The Igniter installer automates steps 1–6 below:

1. Adds the `AshMultiAccount` extension to your User resource
2. Creates the LinkedAccount resource (or patches it if it exists)
3. Registers LinkedAccount in your domain
4. Patches your auth controller's `success/4` to call `put_user_id/3`
5. Creates a `MultiAccountController`
6. Adds `use AshMultiAccount.Phoenix.Router`, the `Plug`, and routes to your router

Steps 7–8 (LiveView hook / controller plug and account switcher component) are app-specific — the installer prints post-install instructions for these.

```sh
mix igniter.install ash_multi_account
```

If `ash_multi_account` is a path or git dependency (not yet on Hex), run the installer directly instead:

```sh
mix ash_multi_account.install
```

You can also specify resource module names explicitly:

```sh
mix igniter.install ash_multi_account \
  --user MyApp.Accounts.User \
  --linked-account MyApp.Accounts.LinkedAccount
```

After running the installer:

- **LiveView apps:** Follow the post-install instructions for the LiveView hook (Step 7) and component (Step 8)
- **Controller-only apps:** Add the `LoadMultiAccount` plug (Step 7 alt) and component (Step 8)

If using a database-backed data layer (AshPostgres, AshSqlite), update the generated LinkedAccount resource's data layer configuration and run migrations:

```bash
mix ash.codegen create_linked_accounts
mix ash.migrate
```

### Manual

Follow all steps below to set up AshMultiAccount manually.

<!-- tabs-close -->

## Step 1: Add the Extension to Your User Resource

Add `AshMultiAccount` to your User resource's extensions and configure the `multi_account` section:

```elixir
defmodule MyApp.Accounts.User do
  use Ash.Resource,
    domain: MyApp.Accounts,
    data_layer: ...,  # any Ash data layer (AshPostgres, AshSqlite, ETS, etc.)
    extensions: [AshAuthentication, AshMultiAccount]

  multi_account do
    linked_account_resource MyApp.Accounts.LinkedAccount
    active_check {:status, :active}
    display_fields [:name, :email, :avatar_url]
    max_linked_accounts 5
  end

  # ... your existing attributes, actions, etc.
end
```

### Configuration Options

| Option | Required | Default | Description |
|--------|----------|---------|-------------|
| `linked_account_resource` | Yes | — | The LinkedAccount resource module |
| `active_check` | No | `nil` | `{field, value}` tuple — only active users can be linked/switched to |
| `display_fields` | No | `[]` | Fields loaded on users for the switcher UI |
| `max_linked_accounts` | No | `5` | Maximum linked accounts per session |

The extension's transformer will automatically add:
- A `:linked_accounts` calculation that resolves linked account records for a session
- A `:get_user_with_linked_accounts` read action used by the LiveView hook

## Step 2: Create the LinkedAccount Resource

Create a new resource with the `AshMultiAccount.LinkedAccount` extension:

```elixir
defmodule MyApp.Accounts.LinkedAccount do
  use Ash.Resource,
    domain: MyApp.Accounts,
    data_layer: ...,  # any Ash data layer (AshPostgres, AshSqlite, ETS, etc.)
    extensions: [AshMultiAccount.LinkedAccount]

  multi_account do
    user_resource MyApp.Accounts.User
  end

  # If using a database-backed data layer, add its config here.
  # For example, with AshPostgres:
  #   postgres do
  #     table "linked_accounts"
  #     repo MyApp.Repo
  #   end
end
```

The transformer generates the full schema automatically:
- **Attributes**: `session_token`, `status` (`:active`/`:inactive`), timestamps
- **Relationships**: `primary_user` and `linked_user` (both `belongs_to` your User)
- **Actions**: `create_linked_account`, `get_linked_accounts`, `activate`, `deactivate`, `read`, `destroy`
- **Calculations**: `is_active?`
- **Identity**: unique constraint on `{primary_user_id, linked_user_id, session_token}`

If using a database-backed data layer, generate and run the migration:

```bash
mix ash.codegen create_linked_accounts
mix ash.migrate
```

> **Note:** In-memory data layers like ETS require no migration step.

## Step 3: Register in Your Domain

Add the LinkedAccount resource to your domain:

```elixir
defmodule MyApp.Accounts do
  use Ash.Domain

  resources do
    resource MyApp.Accounts.User
    resource MyApp.Accounts.LinkedAccount
  end
end
```

## Step 4: Update the Auth Controller

Your AshAuthentication auth controller needs to write the user ID to the session in a format the multi-account hook can read. Add a call to `AshMultiAccount.Phoenix.Session.put_user_id/3` in your success callback:

```elixir
defmodule MyAppWeb.AuthController do
  use MyAppWeb, :controller
  use AshAuthentication.Phoenix.Controller

  def success(conn, _activity, user, _token) do
    conn
    |> store_in_session(user)
    |> AshMultiAccount.Phoenix.Session.put_user_id(user.id)
    |> assign(:current_user, user)
    |> redirect(to: ~p"/")
  end

  def failure(conn, _activity, _reason) do
    conn
    |> put_flash(:error, "Incorrect email or password")
    |> redirect(to: ~p"/sign-in")
  end

  def sign_out(conn, _params) do
    conn
    |> clear_session(:YOUR_OTP_APP)
    |> redirect(to: ~p"/sign-in")
  end
end
```

> **Note:** Replace `:YOUR_OTP_APP` in `clear_session/1` with your OTP application name (the `:app` value in your `mix.exs` project config).

> **Why is `put_user_id` needed?** AshAuthentication stores a JWT subject string in the session. The multi-account hook needs a plain user ID to resolve the current user after account switches. `put_user_id/3` writes the subject in a format both systems can read.

## Step 5: Create the Multi-Account Controller

```elixir
defmodule MyAppWeb.MultiAccountController do
  use MyAppWeb, :controller
  use AshMultiAccount.Phoenix.Controller,
    user_resource: MyApp.Accounts.User

  # Optionally override redirect paths:
  # def after_link_path(_conn), do: ~p"/"
  # def after_switch_path(_conn), do: ~p"/"
  # def sign_in_path(_conn, primary_user_id), do: ~p"/sign-in?return_to=/link/p/#{primary_user_id}"
end
```

The controller mixin provides two actions:
- `link_account/2` — links a newly signed-in user to an existing primary account
- `switch_to_account/2` — switches the session to a different linked user

## Step 6: Add Routes

```elixir
defmodule MyAppWeb.Router do
  use MyAppWeb, :router
  use AshMultiAccount.Phoenix.Router

  pipeline :browser do
    # ... existing plugs ...
    plug AshMultiAccount.Phoenix.Plug
  end

  scope "/", MyAppWeb do
    pipe_through :browser

    # Generates:
    #   GET  /link/p/:primary_user_id  -> MultiAccountController.link_account
    #   POST /link/p/:primary_user_id  -> MultiAccountController.link_account
    #   GET  /link/switch_to/:user_id  -> MultiAccountController.switch_to_account
    multi_account_routes MultiAccountController, MyApp.Accounts.User
  end
end
```

`AshMultiAccount.Phoenix.Plug` ensures a session token UUID exists before any multi-account routes are hit.

## Step 7: LiveView Setup

> **Skip this step** if your app doesn't use LiveView. See [Step 7 alt](#step-7-alt-controller-only-setup) instead.

Add the multi-account hook to your authenticated live sessions. It should run **after** AshAuthentication's hook:

```elixir
live_session :authenticated,
  on_mount: [
    {AshAuthentication.Phoenix.LiveSession, :load_from_session},
    {AshMultiAccount.Phoenix.LiveHook, {:load_multi_account, MyApp.Accounts.User}}
  ] do
  live "/", HomeLive
  # ... more live routes
end
```

The hook sets two assigns on every mount:
- `@current_user` — the user currently acting (may differ from primary after a switch)
- `@primary_user` — the primary account owner (`nil` when not in multi-account mode)

> **Tip:** If your app has both LiveView and controller-rendered pages, you can add the `LoadMultiAccount` plug (Step 7 alt) alongside the LiveView hook. They read the same session keys and coexist without conflict.

## Step 7 alt: Controller-Only Setup

> **Skip this step** if you already added the LiveView hook above and don't have controller-rendered pages that need multi-account assigns.

Add the `LoadMultiAccount` plug to your browser pipeline:

```elixir
pipeline :browser do
  # ... existing plugs ...
  plug AshMultiAccount.Phoenix.Plug
  plug AshMultiAccount.Phoenix.LoadMultiAccount, user_resource: MyApp.Accounts.User
end
```

This plug sets the same `@current_user` and `@primary_user` assigns on `conn` that the LiveView hook sets on the socket. It must run after `:fetch_session` and `AshMultiAccount.Phoenix.Plug`.

For controller-only apps, this is the only integration step needed — the plug handles user resolution and multi-account switching for all controller-rendered pages.

## Step 8: Add the Account Switcher Component

Use the slot-based component in your layout or navigation:

```heex
<AshMultiAccount.Phoenix.Components.account_switcher
  current_user={@current_user}
  primary_user={@primary_user}
>
  <:account :let={account}>
    <.link :if={!account.current?} href={account.switch_url}>
      {account.user.name}
    </.link>
    <span :if={account.current?}>{account.user.name} (current)</span>
  </:account>
  <:add_account :let={url}>
    <.link href={url}>Add another account</.link>
  </:add_account>
</AshMultiAccount.Phoenix.Components.account_switcher>
```

The component imposes no styling — you control all HTML and CSS through slots. It works identically in both LiveView templates and controller-rendered templates — it's a standard `Phoenix.Component` that only needs `@current_user` and `@primary_user` assigns.

## What's Next?

- [How It Works](../topics/how-it-works.md) — understand the data model, session tokens, and linking/switching flows
- [Phoenix Integration](../topics/phoenix-integration.md) — deep dive into each Phoenix module
- [Testing](../topics/testing.md) — set up test support and write tests for multi-account flows
