Authentication for multiple phoenix apps in umbrella

I have three phoenix apps under an umbrella. They have different subdomains all ending with bpmserver.com.
How can I let the authentication work for all phoenix apps? At the moment authentication works for the main app, but I can navigate to the other apps without logging in.
I have authentication for the main phoenix app, for which I took function authenticate_user in a controller from the phoenix tutorial as example.
This function resides in a non-phoenix app in the apps folder, this is the module:

defmodule Auth do
  import Plug.Conn
  import Comeonin.Bcrypt, only: [checkpw: 2, dummy_checkpw: 0]

  def init(opts) do
    Keyword.fetch!(opts, :repo)
  end

  def call(conn, _repo) do
    conn
  end

  def login(conn, user) do
    conn
    |> put_current_user(user)
    |> put_session(:user_id, user.id)
    |> configure_session(renew: true)
  end

  def login_by_username_and_pass(conn, name, given_pass, opts) do
    repo = Keyword.fetch!(opts, :repo)
    user = repo.get_by(Auth.User, name: name)
    cond do
      user && checkpw(given_pass, user.password_hash) ->
        {:ok, login(conn, user)}
      user ->
        {:error, :unauthorized, conn}
     true ->
        dummy_checkpw()
        {:error, :not_found, conn}
    end
  end

  defp put_current_user(conn, user) do
    token = Phoenix.Token.sign(conn, "user socket", user.id)
    conn
    |> assign(:current_user, user)
    |> assign(:user_token, token)
  end

  def logout(conn) do
    configure_session(conn, drop: true)
  end

  def authenticate_user(conn, _opts) do
    if Map.has_key?(conn.assigns, :current_user) && conn.assigns.current_user do
      conn
    else
      conn
      |> Phoenix.Controller.put_flash(:error, "Toegang alleen mogelijk na inloggen")
      |> Phoenix.Controller.redirect(external: "http://www.bpmserver.com/sessions/new")
           # http://www.bpmserver.com will be in an application env var
      |> halt()
    end
  end
end

In the different web.ex sourcefiles the function is included for the controllers:

import Auth, only: [authenticate_user: 2]

In the browser pipeline (main phoenix app) I’m using

defmodule BpmServer.Authentication do
  import Plug.Conn

  def init(opts) do
    Keyword.fetch!(opts, :repo)
  end

  def call(conn, repo) do
    user_id = get_session(conn, :user_id)
    conn.assigns[:link_to_admin]
    conn = assign(conn, :link_to_admin, Application.get_env(:bpm_server, :admin_url))
    conn = assign(conn, :link_to_modeler, Application.get_env(:bpm_server, :modeler_url))

    cond do
      user = conn.assigns[:current_user] ->
        put_current_user(conn, user)
      user = user_id && repo.get(Auth.User, user_id) ->
        put_current_user(conn, user)
      true ->
        assign(conn, :current_user, nil)
    end
  end

  defp put_current_user(conn, user) do
    token = Phoenix.Token.sign(conn, "user socket", user.id)
    conn
    |> assign(:current_user, user)
    |> assign(:user_token, token)
  end
end
4 Likes

believe you have to set domain on the cookie for it to be shared.(http://stackoverflow.com/a/23086139)

Plug.Session has a domain opt so look into that one, though I’m not certain that is the place to do it.

5 Likes

exactly, you need to makes ure the cookie is set with domain option, and the domain value should be: .bpmserver.com, i.e. add the leading dot. I think this is for subset of older browsers to work, but should work on new ones too.

4 Likes

So I should only add

,
domain: ".bpmserver.com"

to the Plug.Session configuration for each endpoint?

It does not work yet. When I inspect, after navigating to one of the not-main phoenix apps, get_session(conn, :user_id) in function authenticate_user (see first message) I get nil and conn does not contain the current_user

For completeness the endpoint of the main phoenix app now contains:

  plug Plug.Session,
    store: :cookie,
    key: "_bpm_server_key",
    signing_salt: "LXEXdgN+",
    domain: ".bpmserver.com"

And the other endpoint:

  plug Plug.Session,
    store: :cookie,
    key: "_bpm_modeler_key",
    signing_salt: "zHsZCI0H",
    domain: ".bpmserver.com"
2 Likes

I don’t know this - fishing here, but I very much suppose the key and salt (and domain) should be identical.

3 Likes

Found it. Indeed key and signing_salt should be the same, but the value for secret_key_base in config.exs also.

Thanks!

7 Likes