I have been working with a LiveView that needs to know who the current user is when they are performing certain operations.

Normally I would have the current user stored in the conn but in this case we can’t fake that out because the conn is transferring the current user to the session which then needs to assign it in the socket.

In this case I am using Guardian for authentication.

This results in code that looks something like this in the LiveView:

 def mount(_params, session, socket) do
    {:ok, assign_new(socket, :current_user, fn -> AuthHelper.load_user!(session) end)}
 end

where AuthHelper is something like:

defmodule AuthHelper do
  @claims %{"typ" => "access"}
  @token_key "guardian_default_token"

  def load_user(%{@token_key => token}) do
    case Guardian.decode_and_verify(MyApp.Guardian, token, @claims) do
      {:ok, claims} ->
         MyApp.Guardian.resource_from_claims(claims)

      _ ->
        {:error, :not_authorized}
    end
  end
  def load_user(_), do: {:error, :not_authorized}

  def load_user!(params) do
    case load_user(params) do
      {:ok, user} -> user
      _ -> nil
    end
  end
end

And this works really well.

But when I was calling tests on a component associated with this LiveView I was running into trouble. I would get errors like:

 ** (ArgumentError) cannot put/delete request cookies after cookies were fetched

or if I tried to recycle the conn myself something like:

** (FunctionClauseError) no function clause matching in Phoenix.LiveViewTest.connect_from_static_token/2

The trick is how to setup authentication in the tests. I needed to be able to set data in the session, but in a way that the session could still be fetched in the future. The function that does this is: Plug.Test.init_test_session/2

 defp login(conn) do
 // Insert a user, in my case I am user a factory (ex_machina).
    user = insert(:user)
    // Get an auth token to put into the session.
    {:ok, auth_token, _} = MyApp.Guardian.encode_and_sign(user, %{})
 // Put the token into the session.
    Plug.Test.init_test_session(conn, guardian_default_token: auth_token)
  end

And with that Phoenix and LiveView are able to properly get the current user into the session and subsiquently into the socket.

Be safe. And take care of each other.

– MM


Related Blogs