Pow: Robust, modular, extendable user authentication and management system

Oh now I see it works as expected for pages that require authentication…
The problem is only about pages that do not require authentication but I render a conditional login or logout link on them. Logout shows up on them even if the user logget out but clicked the back button.

So the solution would myabe to have two different pages for this case even if there are almost the same. The second will pass through Pow authentication_required plug.

But this means to duplicate more or less a lot of pages. If someone has a nicer solution, I’m all for. ^^

Are you using https://github.com/danschultzer/pow#current-user-and-sign-out-link to render the button?

I’m using the template assignment @current_user to check if user is authenticated and to render the buttons I use Routes.pow_session_path/2.
I think it is equivalent to what is said in your link.
Anyway I just pasted exactly the code of the link in my template and I get the same behaviour…

here is my initial code to show login or logout button:

<%= if @current_user do %>
  <%= link @current_user.alias, to: Routes.my_profile_path(@conn, :show) %>
  <%= link "Déconnexion", to: Routes.pow_session_path(@conn, :delete), method: :delete %>
<% else %>
  <%= link "Connexion", to: Routes.pow_session_path(@conn, :new) %>
<% end %>

I should admit that admit it is not a big problem since clicking on the sign_out button won’t make any trouble… The visitor is just redirected to the login page. Another solution I found while googling is to use some js code to disable the browser back button after user logged out.

You need to set the cache control header to prevent the browser cache. You can run Plug.Conn.put_resp_header(conn, "cache-control", "no-cache, no-store, must-revalidate") in a plug for the relevant controllers/actions, or in a plug for the whole endpoint. Back button will fetch cached page unless the server has requested that the page doesn’t get cached.

2 Likes

This is the perfect solution for me. I think I will write a plug that check if the user is authenticated then and only then send the header that tells the browser not to cache the given pages

Thanks for pointing me in the right direction.

I would like to preload an association on authenticated user.
Please where can I add the preload instruction for Pow to call it each time an user authenticate?

Edit:
The purpose is to be able to use something like @current_user.profile.avatar in some template later.

I add a custom autheticate/1 to my user context but now I have this error when trying to authenticate:

# protocol Ecto.Queryable not implemented for %Core.Accounts.User

Core.Accounts.User is my user schema

Here is the custom autheticate/1 function:

  def authenticate(params) do
    user = pow_authenticate(params)

    cond do
      user == nil -> nil
      true -> user |> preload(:profile)
    end
  end

Please any suggestion on how to achieve what I to implement (loading child association on user)?

Edit:

Now it is okay :laughing:

I was calling Ecto.Query.preload instead of Repo.preload.

Here is the correct custom function that finally works.

  def authenticate(params) do
    user = pow_authenticate(params)

    cond do
      user == nil -> nil
      true -> Repo.preload(user, :profile)
    end
  end

Maybe it is a good idea to add this in some guide to help newbies that want to preload associations with authenticated user.

2 Likes

Great job! However, I would recommend a plug like this to make it easier for you instead of the custom authenticate/1 method:

defmodule MyAppWeb.LoadProfilePlug do
  @doc false

  @spec init(any()) :: any()
  def init(opts), do: opts

  @doc false
  @spec call(Conn.t(), atom()) :: Conn.t()
  def call(conn, _opts) do
    config = Pow.Plug.fetch_config(conn)

    case Pow.Plug.current_user(conn, config) do
      nil ->
        conn

      user ->
        preloaded_user = MyApp.Repo.preload(user, :profile)

        Pow.Plug.assign_current_user(conn, preloaded_user, config)
    end
  end
end

And then you can call it across your whole endpoint (you could also set it on just the routes where you know will require the profile preloaded):

defmodule MyAppWeb.Endpoint do
  use Phoenix.Endpoint, otp_app: :my_app

  # ...

  plug Plug.Session,
    store: :cookie,
    key: "_my_app_key",
    signing_salt: "secret"

  plug Pow.Plug.Session, otp_app: :my_app

  plug MyAppWeb.LoadProfilePlug

  # ...
end

If you use a custom authenticate/1 method, then you’ll store the profile along with the user struct in the credentials cache so you have to make sure that all updates of the user struct also has the preloaded profile. This means that you should at least also add a custom update/2 method there, since if users update their profile through the pow registration controller, the cached user struct will be updated with the returning struct from update/2. By using a plug you can completely eliminate this since you’ll fetch the profile directly from the DB each time (and this also prevents that the profile can become out of date if there is some action that may update it, but doesn’t refresh the user in the credentials cache).

4 Likes

Hi,

Newbie pow question here! (Thanks for the awesome work on this by the way)

I just read around a bit about pow and it looks really nice.
I’m working my way trough my first real Phoenix project. I would like to have accounts authentication with users and parent organizations. I have seen in the documentation some examples that mention this (in the invitation section). Is there any introduction level tutorial on how to set it up correctly?

I tried using the mix pow.install with organizations option.

How do you proceed to add actual users from there? I’m trying to find the “recommended” ways for doing these things before hacking my way in and making obvious mistakes.

I can contribute to documentation on this aspect if it’s helpful.

Thanks!

That example is just for custom user schemas. The example is misleading though with the organizations table, and I’ve changed them to mix pow.install -r MyApp.Repo Accounts.Account accounts.

There is no recommended way, but I’ve added some step-by-step instructions here to get you started: Guide on how to set up organization · Issue #11 · pow-auth/pow_site · GitHub

It would be great to have a guide about this on powauth.com, so feel free to comment on the above issue with your experience :slight_smile:

1 Like

Hi!

Thanks for this library, it’s a really nice mix of flexible and batteries-included. I’m finding myself struggling a little with a multi-tenancy situation that isn’t using PG schemas - I wonder if you wouldn’t mind giving your thoughts?

Essentially User belongs to Account, but User.email is only unique within the Account. Accounts are segmented by the accessing domain, and there’s some plug middleware in place that pulls the correct Account into the conn.private storage, but I can’t work out a way of ensuring that this gets merged into the User Context for queries etc within Pow without re-implementing the entire context class from scratch. This is an option, but it’s a bit of a rubbish one :slight_smile: prefix in repo_opts gets special cased, but would you be interested in a PR that maybe pulled a Ecto queryable or the like from the conn which could be merged in to scope User accesses?

If there’s a system for this already then apologies for having missed it, would love to know what you think!

I would recommend using the prefix option in repo_opts. You can use triplex to make it easy: https://hexdocs.pm/pow/multitenancy.html#content

If you want to handle it yourself by limiting scope, then there is really no way around it. You’ll have to set up a custom context module and custom changeset. A queryable wouldn’t work as for example authenticate uses Repo.get_by/3.

The problem here is that Operations doesn’t provide the config to custom Context implementations, otherwise this would be fine. Schemas/tenants is a perfectly fine approach for mutli-tenancy with small numbers of tenants but proven to be unmaintainable once in the thousands, which is why a more straightforward referential namespacing is desirable.

Would you be open to a PR that conditionally supplies the config to a custom user_context if the functions support the extra argument? It seems strange to me that Pow.Ecto.Context receives the argument but an implementation of it doesn’t.

Oh I see.

That’s a good question on Pow.Operations! I’ve created an issue to address that on Github where I also explain the reasoning for not passing config along: https://github.com/danschultzer/pow/issues/283

Feel free to join in there :smile:

One solution that would work with the current Pow release is if the params include tenant (e.g. the login/registration form also has a hidden tenant id field). In that case the custom users context would work, and I believe this is the easiest solution. Otherwise you would have to take full control over the flow with custom controllers.

Edit: I think the caveat with thousands of tenants may also be great to add to the multitenancy guide as alternative to repo_opts! After we figure out a solution I would be happy if you could share the solution with me to add to the guide.

Thanks for that - I’ll pick this up with you over in Github! :slight_smile:

1 Like

Pow 1.0.14 released

Changelog: Github
Hex: https://hex.pm/packages/pow/1.0.14

Session fingerprint and custom metadata

The big new feature is that you can now add custom metadata to the sessions. This paves the road to activity log and session management, and easy use of sessions in LiveView.

By default the session metadata will include a fingerprint that will persist between session renewals. It is also used by PowPersistentSession when creating a new session. New sessions created will invalidate any previous sessions that has the same fingerprint for increased security.

Read more in the docs to see examples such as how to store user agent and IP for a session:
https://hexdocs.pm/pow/1.0.14/Pow.Plug.Session.html#module-custom-metadata

Production checklist

Added a production checklist so you can ensure your app is ready for production run: https://hexdocs.pm/pow/1.0.14/production_checklist.html

Refactored store logic

The stores in Pow now accepts term as keys rather than just binary. That is much more Erlang friendly and efficient (closer to how ETS/Mnesia works), and enable lookups like this:

:ets.match(tab, {[:namespace, :key_1, :_], :"_"})

Pow is backwards compatible so it won’t break existing setups. The Redis cache guide has been updated to use the new setup.

The backend stores are now expected to be able to insert multiple objects at once to keep the operation atomic and isolated. This is pretty close to the ETS API and improves Pow.Store.CredentialsCache.put/3 integrity as it inserts three objects at once.

Happy coding :rocket:

9 Likes

Is there any examples available for Pow Extensions like Invite in API level?

For PowEmailConfirmation and PowResetPassword there are: Add full API guide with extensions like PowEmailConfirmation and PowResetPassword · Issue #14 · pow-auth/pow_site · GitHub

None for PowInvitation, but it should be easy to implement. You just have to set up controllers and call PowInvitation.Plug.create_user/2 and PowInvitation.Plug.update_user/2. You can find more here and here.

1 Like

Hi @danschultzer, i’m trying to make a dummy project like the document: https://hexdocs.pm/pow/api.html#content and getting into the error:

[error] #PID<0.421.0> running WarehouseApiWeb.Endpoint (connection #PID<0.420.0>, stream id 1) terminated
Server: localhost:4000 (http)
Request: POST /api/registration
** (exit) an exception was raised:
    ** (FunctionClauseError) no function clause matching in Pow.Store.CredentialsCache.put/3
        (pow) lib/pow/store/credentials_cache.ex:70: Pow.Store.CredentialsCache.put([backend: Pow.Store.Backend.EtsCache], "450228a1-d8d2-4e11-a68f-17ac373d6fb3", %WarehouseApi.Users.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, confirm_password: nil, current_password: nil, email: "bob3@example.com", id: 8, inserted_at: ~N[2019-11-20 18:20:41], password: nil, password_hash: "$pbkdf2-sha512$100000$Y6t7vwfYiuFO6ZDFcMCDfg==$Rex3X/+yQWrTvXR4XKRgr0P4igCmhQrZYcTiqHerDoMZ4Rdc3+OPjJNPAlIbkftUv9bHNx50hTP+N7jmRKqUog==", updated_at: ~N[2019-11-20 18:20:41]})
        (warehouse_api) lib/warehouse_api_web/api_auth_plug.ex:38: WarehouseApiWeb.ApiAuthPlug.create/3
        (warehouse_api) lib/warehouse_api_web/api_auth_plug.ex:5: WarehouseApiWeb.ApiAuthPlug.do_create/3
        (pow) lib/pow/plug.ex:174: Pow.Plug.maybe_create_auth/3
        (warehouse_api) lib/warehouse_api_web/controllers/registration_controller.ex:11: WarehouseApiWeb.RegistrationController.create/2
        (warehouse_api) lib/warehouse_api_web/controllers/registration_controller.ex:1: WarehouseApiWeb.RegistrationController.action/2
        (warehouse_api) lib/warehouse_api_web/controllers/registration_controller.ex:1: WarehouseApiWeb.RegistrationController.phoenix_controller_pipeline/2
        (phoenix) lib/phoenix/router.ex:288: Phoenix.Router.__call__/2
        (warehouse_api) lib/warehouse_api_web/endpoint.ex:1: WarehouseApiWeb.Endpoint.plug_builder_call/2
        (warehouse_api) lib/plug/debugger.ex:122: WarehouseApiWeb.Endpoint."call (overridable 3)"/2
        (warehouse_api) lib/warehouse_api_web/endpoint.ex:1: WarehouseApiWeb.Endpoint.call/2
        (phoenix) lib/phoenix/endpoint/cowboy2_handler.ex:42: Phoenix.Endpoint.Cowboy2Handler.init/4
        (cowboy) /Users/anduong/warehouse_api/deps/cowboy/src/cowboy_handler.erl:41: :cowboy_handler.execute/2
        (cowboy) /Users/anduong/warehouse_api/deps/cowboy/src/cowboy_stream_h.erl:320: :cowboy_stream_h.execute/3
        (cowboy) /Users/anduong/warehouse_api/deps/cowboy/src/cowboy_stream_h.erl:302: :cowboy_stream_h.request_process/3
        (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3

it looks like the update of Pow.Store.CredentialsCache.put/3.
Thanks!