pow-auth / pow_site

Website for Pow
https://powauth.com
MIT License
4 stars 2 forks source link

Guide on how to handle multiple user schema in the same connection #17

Closed joepstender closed 4 years ago

joepstender commented 4 years ago

This seems to address the fundamental authentication/user management problem that we want to be able to have different kinds of users, for example admin and regular users, teachers and students, business owners and clients, so they can access and do different stuff. Isn't using different user schema just one solution to the problem, using roles and user profiles being the other? What should this guide cover?

Anyway, for now:

From: https://github.com/danschultzer/pow/issues/237#issuecomment-511448140 and https://github.com/danschultzer/pow/issues/19

The current recommended way of dealing with this is to set up an umbrella app with a separate Phoenix app for each user context (all keys will automatically have the otp app namespace, and you'll have clear separation).

You can still deal with it in the same Phoenix app, but in that case you will have to ensure that all admin routes has the admin configuration as the primary config.

It's assume that you would like to have access to the admin user anywhere, so first we need the two plug calls in the endpoint:

defmodule MyAppWeb.Endpoint do
  # ...

  plug Pow.Plug.Session,
    repo: MyApp.Repo,
    user: MyApp.Users.Admin,
    current_user_assigns_key: :current_admin,
    session_key: "admin_auth"

  plug Pow.Plug.Session,
    repo: MyApp.Repo,
    user: MyApp.Users.User

  plug BaseAppWeb.Router
end

Then we'll need to ensure that only the admin config is used for admin Pow paths:

  pipeline :pow_admin do
    plug :set_pow_config,
      repo: MyApp.Repo,
      user: MyApp.Users.Admin,
      current_user_assigns_key: :current_admin,
      session_key: "admin_auth",
      routes_backend: MyAppWeb.Pow.AdminRoutes,
      plug: Pow.Plug.Session
  end

  defp set_pow_config(conn, config), do: Pow.Plug.put_config(conn, config)

  scope "/admin", as: :admin do
    pipe_through [:browser, :pow_admin]
    pow_routes()
  end

And we'll have to ensure all paths generated within that pipeline are the admin paths. Something like this should work (it will prepend the controller module name with Admin so that admin_pow_* route helpers are called instead of the pow_* routes):

defmodule MyAppWeb.Pow.AdminRoutes do
  use Pow.Phoenix.Routes

  def path_for(conn, verb, vars \\ [], query_params \\ []) do
    plug = Module.concat(["AdminPow", verb])
    Pow.Phoenix.Routes.path_for(conn, plug, vars, query_params)
  end

  def url_for(conn, verb, vars \\ [], query_params \\ []) do
    plug = Module.concat(["AdminPow", verb])
    Pow.Phoenix.Routes.url_for(conn, plug, vars, query_params)
  end
end

You can put the admin Pow config in your config.exs and load them in both places on compile time with Application.get_env/2.

Be advised that I haven't tested the above, but this is more or less what you need to do to get it working. There are some caveats, so I recommend to just use an umbrella setup if possible.

danschultzer commented 4 years ago

Sorry for the delay answering was traveling the previous week and been catching up ever since. I did add a guide for it here https://powauth.com/guides/2019-09-12-multiple-schemas.html

joepstender commented 4 years ago

Ah right!