How to pass credentials to LiveView for further usage in subsequent HTTP requests

Hi everyone!

I have a “working prototype”, but am not quite 100% sure if I missed some crucial security implications - so here I am asking again for your suggestions. :wink:

A little bit of context

Our Phoenix application reverse proxies a CouchDB (beside other things). Users (archaeologists) work in an Electron Desktop client that uses PouchDB and sync projects through the Phoenix endpoint. Each project resides in its own database within a single CouchDB instance.

What I want to to do

To get a feel of LiveView, my goal was to implement a monitoring view for a given project that checks for data inconsistencies and let’s us give feedback to our users. I am not 100% sure if my solution of handling the credentials is really save.

** Implementation so far **

In my endpoint.ex I set up the session options as follows:

  @session_options [
(...)
    signing_salt: "<value>",
    encryption_salt: "<another value>"
  ]

  socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]]

And I created my own authentication plug like this:


  def api_auth(%{params: %{"project" => project}} = conn, _) do

    with {name, password} <- Plug.BasicAuth.parse_basic_auth(conn),
      :ok <- FieldHub.CouchService.authenticate(project, %Credentials{name: name, password: password}) do
        conn
        |> fetch_session()
        |> put_session(:user, name)
        |> put_session(:password, password)
      else
        _ ->
          conn
          |> Plug.BasicAuth.request_basic_auth()
          |> halt()
      end
  end
end

To then setup my live view like this:

defmodule FieldHubWeb.MonitoringLive do
(...)
  def mount(%{"project" => project}, %{"user" => user, "password" => password}, socket) do

    credentials =
      %CouchService.Credentials{
        name: user,
        password: password
      }

   Process.send(self(), :update, [])

    {
      :ok,
      socket
      |> assign(:stats, :loading)
      |> assign(:project, project)
      |> assign(:credentials, credentials)
    }
  end

  def handle_info(:update, %{assigns: %{credentials: credentials, project: project}} = socket) do

    stats =
      credentials
      |> Statistics.get_for_project(project)

    Process.send_after(self(), :update, 1000)

    {:noreply, assign(socket, :stats, stats)}
  end
(...)

Further down, the Statistics module triggers a HTTP request towards the CouchDB:

  def get_db_infos(%Credentials{} = credentials, project_name) do
    response =
      HTTPoison.get!(
        "#{base_url()}/#{project_name}",
        headers(credentials)
      )
    case response do
      %{status_code: 200, body: body} ->
        Jason.decode!(body)
      %{status_code: 401} ->
        {:error, 401}
      %{status_code: 403} ->
        {:error, 403}
    end
  end

So I want the username/password in my live view to run the CouchDB requests periodically. My main concern is basically |> put_session(:password, password) in the plug, which feels dangerous.

Did I miss something fundamental security wise? Or even if this is ok: Is my solution nevertheless an antipattern in some other respect?

I opted to rework the general setup based on what phx.gen.auth generates for ecto and PostgreSQL, basically stripping everything out that did not fit.