# Users & Authentication

## Users

The `user.ex` schema looks like this:

{% code title="user.ex" %}

```elixir
defmodule PetalPro.Accounts.User do
  ...
  
  schema "users" do
    field :name, :string
    field :email, :string
    field :password, :string, virtual: true, redact: true
    field :hashed_password, :string, redact: true
    field :confirmed_at, :naive_datetime
    field :is_admin, :boolean, default: false
    field :avatar, :string
    field :last_signed_in_ip, :string
    field :last_signed_in_datetime, :utc_datetime
    field :is_subscribed_to_marketing_notifications, :boolean, default: true
    field :is_suspended, :boolean, default: false
    field :is_deleted, :boolean, default: false
    field :is_onboarded, :boolean, default: false

    timestamps()
  end
  
  ...
end
```

{% endcode %}

Users have some extra fields not included by `phx.gen.auth`:

<table><thead><tr><th width="248.33333333333331">Field</th><th width="150">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>:string</code></td><td>A users full name</td></tr><tr><td><code>avatar</code></td><td><code>:string</code></td><td>A URL to the users avatar image</td></tr><tr><td><code>last_signed_in_ip</code></td><td><code>:string</code></td><td>The IP address of the last login by this user.</td></tr><tr><td><code>is_subscribed_to_marketing_notifications</code></td><td><code>:boolean</code></td><td>Track whether a user wants to receive marketing emails or not.</td></tr><tr><td><code>is_admin</code></td><td><code>:boolean</code></td><td>Admins get access to a special dashboard where they can modify users, see logs, etc.</td></tr><tr><td><code>is_suspended</code></td><td><code>:boolean</code></td><td>An admin can suspend a user, preventing them from logging in.</td></tr><tr><td><code>is_deleted</code></td><td><code>:boolean</code></td><td>Allows for soft deletion of users</td></tr><tr><td><code>is_onboarded</code></td><td><code>:boolean</code></td><td>Track whether or not a user has seen an onboarding screen after registering.</td></tr></tbody></table>

## Authentication

We used [phx.gen.auth](https://hexdocs.pm/phoenix/mix_phx_gen_auth.html) (email/password) and modified the templates to use Tailwind and Petal Components.

### Setting and accessing the current user&#x20;

#### Controller actions

For controller actions we use the plug provided by `mix phx.gen.auth` to set `conn.assigns.current_user` .&#x20;

{% code title="user\_auth.ex" %}

```elixir
defmodule PetalProWeb.UserAuth do
  ...
  
  def fetch_current_user(conn, _opts) do
    {user_token, conn} = ensure_user_token(conn)
    user = user_token && Accounts.get_user_by_session_token(user_token)
    assign(conn, :current_user, user)
  end
  
  ...
end
```

{% endcode %}

You can see the `:fetch_current_user` plug used in the `:browser` pipeline in the router.

{% code title="router.ex" %}

```elixir
defmodule PetalProWeb.Router do
  ...

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, {PetalProWeb.LayoutView, :root}
    plug :protect_from_forgery
    plug :put_secure_browser_headers
    plug :fetch_current_user
    plug PetalProWeb.SetLocalePlug, gettext: PetalProWeb.Gettext
    plug :set_color_scheme
  end
  
  ...
end
```

{% endcode %}

If you want to enforce the user then you can use the `:require_authenticated_user` plug.

{% code title="router.ex" %}

```elixir
scope "/", PetalProWeb do
  pipe_through [:browser, :require_authenticated_user]
  
  # Routes that require a logged in user go here
end
```

{% endcode %}

#### Live views

We can't rely on our plugs in live views, since live views connect over web sockets and avoid the traditional request/response lifecycle. However, a live view will have access to the session, which contains the user token set upon login. Hence, in the live view mount we can use the token to find the `user_token` set in our database for that users session, and from there obtain the logged in user.

Instead of doing this on every live view `mount` function, we can extract this out into an [on\_mount](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#on_mount/1) function and then apply it in the router, like a mini pipeline.

{% code title="user\_on\_mount\_hooks.ex" %}

```elixir
defmodule PetalProWeb.UserOnMountHooks do
  ...

  def on_mount(:require_authenticated_user, _params, session, socket) do
    socket = maybe_assign_user(socket, session)

    if socket.assigns.current_user do
      {:cont, socket}
    else
      {:halt, redirect(socket, to: Routes.user_session_path(socket, :new))}
    end
  end
  
  defp maybe_assign_user(socket, session) do
    assign_new(socket, :current_user, fn ->
      get_user(session["user_token"])
    end)
  end
  
  ...
end
```

{% endcode %}

{% code title="router.ex" %}

```elixir
defmodule PetalProWeb.Router do
  ...
  
  scope "/", PetalProWeb do
    pipe_through [:browser, :require_authenticated_user]

    live_session :protected, on_mount: {PetalProWeb.UserOnMountHooks, :require_authenticated_user} do
      # Live routes that require a logged in user go here
    end
  end
  
  ...
end
```

{% endcode %}
