# Getting Started Ash Authentication Phoenix

In this step-by-step tutorial we create a new empty `Example` Phoenix + Ash application which provides the functionality for authentication. For beginners it is the best to follow the tutorial in the given order. For more advanced users it is a good reference to pick and choose from.

We assumes that you have [Elixir](https://elixir-lang.org) version 1.14.x (check with `elixir -v`) and Phoenix 1.7 (check with `mix phx.new --version`) installed. We also assume that you have a [PostgreSQL](https://www.postgresql.org) database running which we use to persist the user data.

## Green Field Phoenix Application

We start with a new Phoenix application:

```bash
$ mix phx.new example
$ cd example
```

## Basic Ash Setup

### Application Dependencies

We need to add the following dependencies. Use `mix hex.info dependency_name` to get the latest version of each dependency.

**mix.exs**

```elixir
defmodule Example.MixProject do
  use Mix.Project
  # ...

    defp deps do
    [
      # ...
      # add these lines -->
      {:ash, "~> x.x"},
      {:ash_authentication, "~> x.x"},
      {:ash_authentication_phoenix, "~> x.x"},
      {:ash_postgres, "~> x.x"}
      # <-- add these lines
    ]
  end
  # ...
```

Let's fetch everything:

```bash
$ mix deps.get
```

### Formatter

We can make our life easier and the code more consistent by adding formatters to the project. We will use [Elixir's built-in formatter](https://hexdocs.pm/mix/master/Mix.Tasks.Format.html) for this.

**.formatter.exs**

```elixir
[
  import_deps: [
    :phoenix,
    # add these lines -->
    :ash,
    :ash_authentication_phoenix,
    :ash_postgres
    # <-- add these lines
    ],
  plugins: [Phoenix.LiveView.HTMLFormatter],
  inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}"]
]
```

### Phoenix 1.7 compatibility

For Phoenix 1.7 we need to change `helpers: false` to `helpers: true` in the router section:

**lib/example_web.ex**

```elixir
defmodule ExampleWeb do
# ...
  def router do
    quote do
      use Phoenix.Router, helpers: true # <-- Change this line
    # ...
```

### Tailwind

If you plan on using our default [Tailwind](https://tailwindcss.com/)-based
components without overriding them you will need to modify your
`assets/tailwind.config.js` to include the `ash_authentication_phoenix`
dependency:

**assets/tailwind.config.js**

```javascript
// See the Tailwind configuration guide for advanced usage
// https://tailwindcss.com/docs/configuration

const plugin = require("tailwindcss/plugin");

module.exports = {
  content: [
    "./js/**/*.js",
    "../lib/*_web.ex",
    "../lib/*_web/**/*.*ex",
    "../deps/ash_authentication_phoenix/**/*.ex", // <-- Add this line
  ],
  theme: {
    extend: {
      colors: {
        brand: "#FD4F00",
      },
    },
  },
  plugins: [
    require("@tailwindcss/forms"),
    plugin(({ addVariant }) =>
      addVariant("phx-no-feedback", [".phx-no-feedback&", ".phx-no-feedback &"])
    ),
    plugin(({ addVariant }) =>
      addVariant("phx-click-loading", [
        ".phx-click-loading&",
        ".phx-click-loading &",
      ])
    ),
    plugin(({ addVariant }) =>
      addVariant("phx-submit-loading", [
        ".phx-submit-loading&",
        ".phx-submit-loading &",
      ])
    ),
    plugin(({ addVariant }) =>
      addVariant("phx-change-loading", [
        ".phx-change-loading&",
        ".phx-change-loading &",
      ])
    ),
  ],
};
```

## AshPostgres.Repo Setup

We use [AshPostgres](https://hexdocs.pm/ash_postgres/AshPostgres.html) to handle the database tables for our application. We need to replace the content of the `Repo` module with the following code:

**lib/example/repo.ex**

```elixir
defmodule Example.Repo do
  use AshPostgres.Repo, otp_app: :example

  def installed_extensions do
    ["uuid-ossp", "citext"]
  end
end
```

We have to configure the Repo in `config/config.exs`. While doing that we also configure other stuff which we need later.

**config/config.exs**

```elixir
# ...

import Config

# add these lines -->
config :example,
  ash_apis: [Example.Accounts]

config :ash,
  :use_all_identities_in_manage_relationship?, false
# <-- add these lines

# ...
```

We need to add `AshAuthentication.Supervisor` to the supervision tree in `lib/example/application.ex`:

`** lib/example/application.ex **`

```elixir
defmodule Example.Application do
  # ...

  @impl true
  def start(_type, _args) do
    children = [
      # ...
      # add this line -->
      {AshAuthentication.Supervisor, otp_app: :example}
      # <-- add this line
    ]
  # ...
```

## Accounts Api and Resources

We need to create an `Accounts` Api in our application to provide a `User` and a `Token` resource. Strictly speaking we don't need the `Token` resource for just the login with a password. But we'll need it later (e.g. for the password reset) so we just create it now while we are here.

At the end we should have the following directory structure:

```bash
lib/example
├── accounts
|   ├── accounts.ex
|   └── resources
│       ├── registry.ex
│       ├── token.ex
|       └── user.ex
...
```

**lib/example/accounts/accounts.ex**

```elixir
defmodule Example.Accounts do
  use Ash.Api

  resources do
    registry Example.Accounts.Registry
  end
end
```

**lib/example/accounts/resources/user.ex**

```elixir
defmodule Example.Accounts.User do
  use Ash.Resource,
    data_layer: AshPostgres.DataLayer,
    extensions: [AshAuthentication]

  attributes do
    uuid_primary_key :id
    attribute :email, :ci_string, allow_nil?: false
    attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
  end

  authentication do
    api Example.Accounts

    strategies do
      password :password do
        identity_field :email
        sign_in_tokens_enabled? true
      end
    end

    tokens do
      enabled? true
      token_resource Example.Accounts.Token

      signing_secret Example.Accounts.Secrets
    end
  end

  postgres do
    table "users"
    repo Example.Repo
  end

  identities do
    identity :unique_email, [:email]
  end

  # If using policies, add the following bypass:
  # policies do
  #   bypass AshAuthentication.Checks.AshAuthenticationInteraction do
  #     authorize_if always()
  #   end
  # end
end
```

**lib/example/accounts/secrets.ex**

```elixir
defmodule Example.Accounts.Secrets do
  use AshAuthentication.Secret


  def secret_for([:authentication, :tokens, :signing_secret], Example.Accounts.User, _) do
    case Application.fetch_env(:example, ExampleWeb.Endpoint) do
      {:ok, endpoint_config} ->
        Keyword.fetch(endpoint_config, :secret_key_base)
      :error ->
        :error
    end
  end
end
```

**lib/example/accounts/resources/token.ex**

```elixir
defmodule Example.Accounts.Token do
  use Ash.Resource,
    data_layer: AshPostgres.DataLayer,
    extensions: [AshAuthentication.TokenResource]

  token do
    api Example.Accounts
  end

  postgres do
    table "tokens"
    repo Example.Repo
  end

  # If using policies, add the following bypass:
  # policies do
  #   bypass AshAuthentication.Checks.AshAuthenticationInteraction do
  #     authorize_if always()
  #   end
  # end
end
```

Next, let's define our registry:

**lib/example/accounts/registry.ex**

```elixir
defmodule Example.Accounts.Registry do
  use Ash.Registry, extensions: [Ash.Registry.ResourceValidations]

  entries do
    entry Example.Accounts.User
    entry Example.Accounts.Token
  end
end
```

### Create and Migration

Now is a good time to create the database and run the migrations. You have to use specific `ash_postgres` mix tasks for that:

```bash
$ mix ash_postgres.create
$ mix ash_postgres.generate_migrations --name add_user_and_token
$ mix ash_postgres.migrate
```

> In case you want to drop the database and start over again during development you can use `mix ash_postgres.drop` followed by `mix ash_postgres.create` and `mix ash_postgres.migrate`.

## Router Setup

`ash_authentication_phoenix` includes several helper macros which can generate
Phoenix routes for you. For that you need to add 6 lines in the router module or just replace the whole file with the following code:

**lib/example_web/router.ex**

```elixir
defmodule ExampleWeb.Router do
  use ExampleWeb, :router
  # Add this line
  use AshAuthentication.Phoenix.Router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, {ExampleWeb.Layouts, :root}
    plug :protect_from_forgery
    plug :put_secure_browser_headers
    # Add the next line
    plug :load_from_session
  end

  pipeline :api do
    plug :accepts, ["json"]
    # Add the next line
    plug :load_from_bearer
  end

  scope "/", ExampleWeb do
    pipe_through :browser

    get "/", PageController, :home

    # add these lines -->
    sign_in_route()
    sign_out_route AuthController
    auth_routes_for Example.Accounts.User, to: AuthController
    reset_route []
    # <-- add these lines
  end

  # Other scopes may use custom stacks.
  # scope "/api", ExampleWeb do
  #   pipe_through :api
  # end

  # Enable LiveDashboard and Swoosh mailbox preview in development
  if Application.compile_env(:example, :dev_routes) do
    # If you want to use the LiveDashboard in production, you should put
    # it behind authentication and allow only admins to access it.
    # If your application does not have an admins-only section yet,
    # you can use Plug.BasicAuth to set up some basic authentication
    # as long as you are also using SSL (which you should anyway).
    import Phoenix.LiveDashboard.Router

    scope "/dev" do
      pipe_through :browser

      live_dashboard "/dashboard", metrics: ExampleWeb.Telemetry
      forward "/mailbox", Plug.Swoosh.MailboxPreview
    end
  end
end
```

### Generated routes

Given the above configuration you should see the following in your routes:

```
# mix phx.routes

Generated example app
          auth_path  GET  /sign-in                               AshAuthentication.Phoenix.SignInLive :sign_in
          auth_path  GET  /sign-out                              ExampleWeb.AuthController :sign_out
          auth_path  *    /auth/user/password/register           ExampleWeb.AuthController {:user, :password, :register}
          auth_path  *    /auth/user/password/sign_in            ExampleWeb.AuthController {:user, :password, :sign_in}
          page_path  GET  /                                      ExampleWeb.PageController :home
...
```

## AuthController

While running `mix phx.routes` you probably saw the warning message that the `ExampleWeb.AuthController.init/1 is undefined`. Let's fix that by creating a new controller:

**lib/my_app_web/controllers/auth_controller.ex**

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

  def success(conn, _activity, user, _token) do
    return_to = get_session(conn, :return_to) || ~p"/"

    conn
    |> delete_session(:return_to)
    |> store_in_session(user)
    |> assign(:current_user, user)
    |> redirect(to: return_to)
  end

  def failure(conn, _activity, _reason) do
    conn
    |> put_status(401)
    |> render("failure.html")
  end

  def sign_out(conn, _params) do
    return_to = get_session(conn, :return_to) || ~p"/"

    conn
    |> clear_session()
    |> redirect(to: return_to)
  end
end
```

**lib/example_web/controllers/auth_html.ex**

```elixir
defmodule ExampleWeb.AuthHTML do
  use ExampleWeb, :html

  embed_templates "auth_html/*"
end
```

**lib/example_web/controllers/auth_html/failure.html.heex**

```html
<h1 class="text-2xl">Authentication Error</h1>
```

## Example home.html.heex

To see how the authentication works we replace the default Phoenix `home.html.eex` with a minimal example which has a top navbar. On the right side it shows the `@current_user` and a sign out button. If you are not signed in you will see a sign in button.

**lib/example_web/controllers/page_html/home.html.heex**

```html
<nav class="bg-gray-800">
  <div class="px-2 mx-auto max-w-7xl sm:px-6 lg:px-8">
    <div class="relative flex items-center justify-between h-16">
      <div
        class="flex items-center justify-center flex-1 sm:items-stretch sm:justify-start"
      >
        <div class="block ml-6">
          <div class="flex space-x-4">
            <div class="px-3 py-2 text-xl font-medium text-white ">
              Ash Demo
            </div>
          </div>
        </div>
      </div>
      <div
        class="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0"
      >
        <%= if @current_user do %>
        <span class="px-3 py-2 text-sm font-medium text-white rounded-md">
          <%= @current_user.email %>
        </span>
        <a
          href="/sign-out"
          class="rounded-lg bg-zinc-100 px-2 py-1 text-[0.8125rem] font-semibold leading-6 text-zinc-900 hover:bg-zinc-200/80 active:text-zinc-900/70"
        >
          Sign out
        </a>
        <% else %>
        <a
          href="/sign-in"
          class="rounded-lg bg-zinc-100 px-2 py-1 text-[0.8125rem] font-semibold leading-6 text-zinc-900 hover:bg-zinc-200/80 active:text-zinc-900/70"
        >
          Sign In
        </a>
        <% end %>
      </div>
    </div>
  </div>
</nav>

<div class="py-10">
  <header>
    <div class="px-4 mx-auto max-w-7xl sm:px-6 lg:px-8">
      <h1 class="text-3xl font-bold leading-tight tracking-tight text-gray-900">
        Demo
      </h1>
    </div>
  </header>
  <main>
    <div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
      <div class="px-4 py-8 sm:px-0">
        <div
          class="border-4 border-gray-200 border-dashed rounded-lg h-96"
        ></div>
      </div>
    </div>
  </main>
</div>
```

### Start Phoenix

You can now start Phoenix and visit
[`localhost:4000`](http://localhost:4000) from your browser.

```bash
$ mix phx.server
```

### Sign In

Visit [`localhost:4000/sign-in`](http://localhost:4000/sign-in) from your browser.

The sign in page shows a link to register a new account.

### Sign Out

Visit [`localhost:4000/sign-out`](http://localhost:4000/sign-out) from your browser.

## Reset Password

In this section we add a reset password functionality. Which is triggered by adding `resettable` in the `User` resource. Please replace the `strategies` block in `lib/example/accounts/resources/user.ex` with the following code:

**lib/example/accounts/resources/user.ex**

```elixir
# [...]
strategies do
  password :password do
    identity_field :email

    resettable do
      sender Example.Accounts.User.Senders.SendPasswordResetEmail
    end
  end
end
# [...]
```

To make this work we need to create a new module `Example.Accounts.User.Senders.SendPasswordResetEmail`:

**lib/example/accounts/user/senders/send_password_reset_email.ex**

```elixir
defmodule Example.Accounts.User.Senders.SendPasswordResetEmail do
  @moduledoc """
  Sends a password reset email
  """
  use AshAuthentication.Sender
  use ExampleWeb, :verified_routes

  @impl AshAuthentication.Sender
  def send(user, token, _) do
    Example.Accounts.Emails.deliver_reset_password_instructions(
      user,
      url(~p"/password-reset/#{token}")
    )
  end
end
```

We also need to create a new email template:

**lib/example/accounts/emails.ex**

```elixir
defmodule Example.Accounts.Emails do
  @moduledoc """
  Delivers emails.
  """

  import Swoosh.Email

  def deliver_reset_password_instructions(user, url) do
    if !url do
      raise "Cannot deliver reset instructions without a url"
    end

    deliver(user.email, "Reset Your Password", """
    <html>
      <p>
        Hi #{user.email},
      </p>

      <p>
        <a href="#{url}">Click here</a> to reset your password.
      </p>

      <p>
        If you didn't request this change, please ignore this.
      </p>
    <html>
    """)
  end

  # For simplicity, this module simply logs messages to the terminal.
  # You should replace it by a proper email or notification tool, such as:
  #
  #   * Swoosh - https://hexdocs.pm/swoosh
  #   * Bamboo - https://hexdocs.pm/bamboo
  #
  defp deliver(to, subject, body) do
    IO.puts("Sending email to #{to} with subject #{subject} and body #{body}")

    new()
    |> from({"Zach", "zach@ash-hq.org"}) # TODO: Replace with your email
    |> to(to_string(to))
    |> subject(subject)
    |> put_provider_option(:track_links, "None")
    |> html_body(body)
    |> Example.Mailer.deliver!()
  end
end
```

Your new reset password functionality is active. Visit [`localhost:4000/sign-in`](http://localhost:4000/sign-in) with your browser and click on the `Forgot your password?` link to trigger the reset password workflow.

# Next up

- [Use AshAuthentication with LiveView](/documentation/tutorials/use-ash-authentication-with-liveview.md)
