Hi,
I'm following guide for multiple users in same scope:
https://github.com/danschultzer/pow/blob/e725d04b2a9e91565a1c7c1075411e0ae2cec792/guides/MULTIPLE_USERS_IN_SAME_SCOPE.md
My problem is that when I register a new user - no matter via admin scope or via user scope - my app uses only the last Session plug in endpoint.ex file. It seems, that somehow the namespace configuration is not passed.
Here is my router.ex:
...
scope "/", private: %{pow_config_namespace: :user} do
pipe_through [:browser, :locked]
pow_routes()
pow_extension_routes()
pow_assent_routes()
end
scope "/admin", private: %{pow_namespace: :admin}, as: :admin do
pipe_through :browser
pow_routes()
end
scope "/", BaseAppWeb, private: %{pow_namespace: :user} do
pipe_through [:browser, :locked]
get "/", PageController, :index
get "/static", PageController, :show_static
end
scope "/panel", BaseAppWeb do
pipe_through :browser
get "/", AdminController, :index
get "/users", AdminUserController, :index
get "/block_user", AdminUserController, :block_user
scope "/static_pages" do
get "/", StaticPagesController, :index
get "/new_page", StaticPagesController, :new
post "/create_page", StaticPagesController, :create
get "/edit_page", StaticPagesController, :edit
post "/update_page", StaticPagesController, :update
get "/delete_page", StaticPagesController, :delete
end
end
...
and endpoint.ex:
defmodule BaseAppWeb.Endpoint do
use Phoenix.Endpoint, otp_app: :base_app
socket "/socket", BaseAppWeb.UserSocket,
websocket: true,
longpoll: false
plug Plug.Static,
at: "/",
from: :base_app,
gzip: false,
only: ~w(css fonts images js favicon.ico robots.txt)
if code_reloading? do
socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
plug Phoenix.LiveReloader
plug Phoenix.CodeReloader
end
plug Plug.RequestId
plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]
plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Phoenix.json_library()
plug Plug.MethodOverride
plug Plug.Head
plug Plug.Session,
store: :cookie,
key: "_base_app_key",
signing_salt: "salt"
plug Pow.Plug.Session,
repo: BaseApp.Repo,
user: BaseApp.Auth.Admin,
namespace: :admin,
current_user_assigns_key: :current_admin
plug Pow.Plug.Session,
repo: BaseApp.Repo,
user: BaseApp.Auth.User,
namespace: :user,
current_user_assigns_key: :current_user
plug BaseAppWeb.Router
end
In case of this code, no matter if I register from /admin/registration/new or /registration/new, my app always creates BaseApp.Auth.User - because plug with user: BaseApp.Auth.User comes after a plug with user: BaseApp.Auth.Admin in endpoint.ex. In case when I switch their order - it creates only admin users.
That guide is from a deleted PR branch: #24
I rejected it due to https://github.com/danschultzer/pow/issues/19#issuecomment-439668816. 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.
Thank you for a quick answer!
I just wanted to avoid umbrella app, so I followed your instructions. This solution works really good when it comes to adding users to the right table, however in the AdminRoutes module it was necessary to make some slight changes, here is my code:
defmodule MyAppWeb.Pow.AdminRoutes do
use Pow.Phoenix.Routes
def path_for(conn, plug, verb, vars \\ [], query_params \\ []) do
plug = Module.concat(["admin_pow", plug])
Pow.Phoenix.Routes.path_for(conn, plug, verb, vars, query_params)
end
def url_for(conn, plug, verb, vars \\ [], query_params \\ []) do
plug = Module.concat(["admin_pow", plug])
Pow.Phoenix.Routes.url_for(conn, plug, verb, vars, query_params)
end
end
And also although the user is added to the right table - when it comes to admin users I've got a small problem. After creating an admin user I'm becoming this error: Pow plug was not found in config. Please use a Pow plug that puts the `:plug` in the Pow configuration. Probably I'm missing something really goofy, but I can't find damn thing.
Good catch, I've updated the example!
I forgot that the plug is necessary for Pow to update session, it's something that's automatically added to the config when running the plug:
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
This will call the Pow.Plug.Session to renew session 馃槃
At some point I'll write a guide on all this, so please let me know if things are working as expected or if there are any caveats.
Everything works fine now 馃槃
Thank you for help!
Most helpful comment
That guide is from a deleted PR branch: #24
I rejected it due to https://github.com/danschultzer/pow/issues/19#issuecomment-439668816. 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:
Then we'll need to ensure that only the admin config is used for admin Pow paths:
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 thepow_*routes):You can put the admin Pow config in your
config.exsand load them in both places on compile time withApplication.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.