defmodule BirdyChatWeb.Api.Server.Internal.Controller do @moduledoc """ A controller for handling inter-server communication. It started off with using Erlang term format instead of JSON as communication language but then I removed it for the following reasons: 1. The messages are mostly binaries anyway, there is no big efficiency gain from skipping JSON. 2. Testing JSON is much easier than testing erlang term format. 3. Erlang term format can give an illusion of extra security but unless the transport is HTTPS then the communication is still inherently unsafe. 4. Erlang term format is difficult to handle for unfamiliar developers, you need to remember about safe conversion to avoid atom exhaustion attacks or sending an `rm -rf /` function over the wire. The endpoint is protected by simple authentication that requires the secret key of all servers being the same. It is good enough for a demo, but for any real application it would need to be reconsidered. HTTPS would be a non-negotiable requirement for any user-facing deployment. """ use BirdyChatWeb, :controller def create(conn, params) do with true <- authorised?(conn.req_headers, params), {:ok, changeset} <- BirdyChat.Message.validate_for_inter_server_use(params), :ok <- BirdyChat.MessageWriter.write(changeset.changes) do conn |> put_status(:created) |> render(:create, message: changeset.changes) else _any -> conn |> put_status(:forbidden) |> render(:error, message: "Unauthorised") end end defp authorised?(headers, %{"from" => from}) do case Enum.find(headers, fn {key, _value} -> key == "authorization" end) do nil -> false {"authorization", token} -> case Phoenix.Token.verify(BirdyChatWeb.Endpoint, "serverAuth", token, max_age: 1200) do {:ok, id} -> id == from {:error, :invalid} -> false end end end end