# 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 %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.petal.build/petal-pro-documentation/product-docs/fundamentals/users-and-authentication.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
