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.
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?