Dealing with multiple user schemas
by Dan Schultzer on 12 September 2019
Pow can handle multiple user schemas out-of-the-box in umbrella projects, or can be configured to handle it in the same Phoenix endpoint.
In this guide we’ll go through the following three options:
- Use roles to diffentiate the users
- Use umbrella project to keep user structs in different endpoint
- User structs in same endpoint
It’s important that you evaulate your project requirements. Each option gets progressively more complex.
In the following examples we’ll imagine that we work with two types of users: User and admin.
1. User roles
This is the simplest solution as you won’t have to deal with multiple Pow contexts and namespacing. You should head over to the user roles guide in the Pow docs if a roles setup fit your project requirements.
2. Umbrella project
The umbrella project is an easy solution if you have different user structs. Usually the admin and user dashboard are separated, so it’s a natural step to also have them set up as individual Phoenix apps. It may even make development and maintainance easier.
Pow will namespace cookies and sessions with the
:otp_app name, so all you have to do is to set both Phoenix apps up with Pow.
No additional configuration is required:
# apps/my_app_web/config/config.exs config :my_app_web, :pow, repo: MyApp.Repo, user: MyApp.Users.User # apps/my_app_web/lib/my_app_web/endpoint.ex defmodule MyAppWeb.Endpoint do use Phoenix.Endpoint, otp_app: :my_app_web # ... plug Pow.Plug.Session, otp_app: :my_app_web plug MyAppWeb.Router end
# apps/my_app_admin/config/config.exs config :my_app_admin, :pow, repo: MyApp.Repo, user: MyApp.Users.Admin # apps/my_app_admin/lib/my_app_admin/endpoint.ex defmodule MyAppAdmin.Endpoint do use Phoenix.Endpoint, otp_app: :my_app_admin # ... plug Pow.Plug.Session, otp_app: :my_app_admin plug MyAppAdmin.Router end
3. Same endpoint
We’ll have to ensure that everything is namespaced right when we deal with multiple Pow configurations in the same endpoint.
It’s assumed that you already have set up your Phoenix app with one Pow configuration, and we’ll add the second user type (the admin in this example).
First configure the endpoint so we ensure that the admin user can be loaded:
defmodule MyAppWeb.Endpoint do use Phoenix.Endpoint, otp_app: :my_app # ... 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, otp_app: :my_app plug MyAppWeb.Router end
Now we’ll add Pow routes for the admin user:
defmodule MyAppWeb.Router do use MyAppWeb, :router use Pow.Phoenix.Router # ... pipelines 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 "/" do pipe_through :browser pow_routes() end scope "/admin", as: :admin do pipe_through [:browser, :pow_admin] pow_routes() end # ... routes end
We’ll have to ensure all paths generated within that pipeline are admin paths with our custom
:routes_backend module. This module will prepend the controller module name with
Admin so that
admin_pow_* route helpers are called instead of the
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
An umbrella project is still preferred since it’s much easier when dealing with multiple Pow contexts.