Accessing :current_user from liveview form_component.ex?

I am using the following |> put_session(:current_user, user) to make the :current_user available to the liveview, is this the preferred solution?

  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)
    conn
    |> assign(:current_user, user)
    |> put_session(:current_user, user)
  end

I can’t find where I read it but storing in session just the user id is preferred to storing the whole user struct.

There is also this answer in stackoverflow I just found:

Anyway you have a great guide to achieve what you want in the official LiveView docs.

https://hexdocs.pm/phoenix_live_view/security-model.html

The parts I find the most relevant are

You should read the whole guide though. There are several ways to make the current user available in the live sockect. Either in the router or in your live views but they all use the on_mount option/hook to call a module you define for the prurpose.

Not only you will make the user struct available but you will check if the user is authenticated.

An example of such a live module from the docs:

defmodule MyAppWeb.UserLiveAuth do
  import Phoenix.LiveView

  def on_mount(:default, _params, %{"user_id" => user_id} = _session, socket) do
    socket = assign_new(socket, :current_user, fn ->
      Accounts.get_user!(user_id)
    end)

    if socket.assigns.current_user.confirmed_at do
      {:cont, socket}
    else
      {:halt, redirect(socket, to: "/login")}
    end
  end
end

Edit:
Thanks to the light brought by @LostKobrakai on the question we can rewrite the above module to use the user token instead of his id:

defmodule MyAppWeb.UserLiveAuth do
  import Phoenix.LiveView

  def on_mount(:default, _params, %{"user_token" => user_token} = _session, socket) do
    socket = assign_new(socket, :current_user, fn ->
      Accounts.get_user_by_session_token!(user_token)
    end)

    if socket.assigns.current_user.confirmed_at do
      {:cont, socket}
    else
      {:halt, redirect(socket, to: "/login")}
    end
  end
end
1 Like

Generally this advice is totally correct. The session should include the minimal amount of data to be able to refetch everything in the LV process.

More specifically with phx.gen.auth the session should hold the user token and not the user id. Leaking the user id can be a security issue and given the session data is passed to the client it can be exposed. It’s not plain text, but if someone wants to decode the erlang term format they can easily do so. phx.gen.auth already uses a token based approach, so it’s best to continue to do so for LV as well.

2 Likes