Assigns from router plugs not making it to LiveView sockets

Hi! I’m having an issue getting my auth plug to assign a current user so that my LiveView doesn’t load it twice.

defmodule MyAppWeb.Router do
  use MyAppWeb, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, {MyAppWeb.LayoutView, :root}
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :authenticated do
    plug MyAppWeb.Plugs.Authenticated
  end

  scope "/", MyAppWeb do
    pipe_through [:browser, :authenticated]

    live "/", OverviewLive
  end
end

defmodule MyAppWeb.Plugs.Authenticated do
  @behaviour Plug
  import Plug.Conn

  alias MyApp.Accounts

  def init(opts), do: opts

  def call(conn, _) do
    case get_session(conn) do
      %{"user_id" => user_id} ->
        with %User{} = user <- Accounts.get_user(user_id) do
          conn
          |> assign(:current_user, user)
          |> put_session(:user_id, user.id)
        else
          _ ->
            conn
            |> clear_session()
            |> Phoenix.Controller.redirect(to: "/sign-in")
            |> halt()
        end
      _ ->
        conn
        |> clear_session()
        |> Phoenix.Controller.redirect(to: "/sign-in")
        |> halt()
    end
  end
end

defmodule MyAppWeb.OverviewLive do
  use MyAppWeb.BaseLive
  
  alias MyApp.Accounts

  def mount(_params, %{"user_id" => user_id}, socket) do
    socket = socket
    |> assign_new(:current_user, fn ->
      Accounts.get_user(user_id)
    end) # Problem is here, this always runs the function instead of finding an existing current_user value
    {:ok, socket}
  end
end

What I expect is when I visit the OverviewLive route with a valid session, the user is loaded once in the Authenticated plug I have and then in OverviewLive the socket already has it and assign_new doesn’t have to do anything. Instead I can see on my console the current user being queried for twice, one for static mount and one for live mount. I also did some logging of the socket and assigns here and there to confirm that on the second mount the user just isn’t in the socket assigns like I believe it should.

Am I miss understanding anything? Thank you for any help.

On the 2nd mount, when the websocket connection is established, your socket assigns can’t have :current_user since it doesn’t go through your plug. You don’t have access to the Plug.Conn assigns here.

On the 1st mount, that is the http connection, it does go through your plug, and the current user is fetched by your plug and not again in the mount since you’re using assign_new.

If you were not to use assign_new, but instead assign, you would see 3 queries instead of the 2 you see now.

EDIT: this blog post might clear some things: https://kobrakai.de/kolumne/liveview-double-mount/

1 Like

Thanks! That helped me understand a lot I wasn’t sure what I was seeing in my logging but now I do and I know how things flow between static render and after the websocket is connected.