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

I was curious if there was a way to configure the user module that the context references more dynamically based on which application hits the application hits the context module?

It’s possible if you are able to tell what struct to use based on the params, but you’ll have to override all the methods. Also in this case you should just use the @behaviour instead of the macro.

Feel free to open an issue on github to share your setup, there may be alternatives depending on your exact use case.

Thank you so much! It is easy and explicit for us to pull out a separate context for this separate set of users so no problem, was just curious. Thanks Dan!

Announcements

Sponsorship

For the last year I’ve been working full time on open source using my own funds. It’s been a great experience, so now I’m experimenting with GitHub Sponsors to see how this could work longer term. If you use Pow for your business and would like to show support, here’s the link: https://github.com/sponsors/danschultzer

Pow Business

I’m working on a MVP for an identity provider built on Pow: https://powauth.com/business/.

It’ll target smaller startups and developers. I would love to hear from you if you are interested in this solution.

Pow 1.0.16 and 1.0.17 released

Changelog: Github

Hex: https://hex.pm/packages/pow/1.0.17

These were two meaty releases that mostly has a lot of under-the-hood improvements.

Prevent user enumeration

Pow now protects against user enumeration attacks when PowEmailConfirmation extension is enabled. As an example, this means that instead of an error message being shown if an e-mail is already taken, the user will see a message that informs them to check their inbox to confirm the e-mail the same way as if the registration succeeded.

Improved documentation

Added security practices guide, and updated the production checklist with an e-mail rate limitation section.

Thanks for all the contributions and feedback :rocket:

12 Likes

Dan,

Thanks for developing Pow and Pow Ascent, it’s great to have such a good framework for authentication in Elixir.

I had a question on unlinking from auth provider like Google, what is the expected result? It redirects to the change password page, I was expecting it to leave the user record and ask to set a password, but maybe I misunderstood the flow.

Thanks! It’ll always redirect to the registration edit page, either with an :error or :info flash: https://github.com/pow-auth/pow_assent/blob/v0.4.5/lib/pow_assent/phoenix/controllers/authorization_controller.ex#L154-L164

Deletion will always fail if no password is set and there’s only one identity for the user.

I made a screencast on pow & pow_assent that might be helpful

15 Likes

Hi!,

I’m trying to integrate Pow with an API following the API guide docs. I was wondering if there’s a way to stop Pow.Plug.create_user ( which is used from a RegistrationController ) from creating a new session if the user creation is successful. I want to just send a confirmation email and redirect to my login page client-side instead.

I can’t find any callback within Pow.Plug.Base which lets me do that.

  1. I can create the user, but not return the struct back to Pow from APIAuthPlug(custom plug to replace Pow.Plug.Session), which won’t create a session for the user automatically.
  2. Another option is to write a custom function, but then I’d be out of Pow’s flow of things and it might get too complicated.

I think both options are bad. I tried to trace the code flow through Pow and it seems like Pow.Plug.create_users calls into Pow.Plug.create/3, which in turn ends up at APIAuthPlug.create.

How should I go about this? Any help is appreciated!

Thanks!

Edit:
I can just copy Pow.Plug.create_user function in my RegistrationController and do something like this…

defmodule RegistrationController do
  def create(conn, %{ "user" => params}) do
    conn
    |> custom_create(params)
    |> case do
       {:ok, user, conn} -> 
          send_confirmation_email()
          json(conn, %{message: "Please check your email"}

       {:error, changeset, conn} -> ....
    end
  end

  # Copied from Pow.Plug.create_user/2
  def custom_create(conn, params) do
    alias Pow.Plug

    config = Plug.fetch_config(conn)
    
    params
    |> Pow.Operations.create(config)
  end
end

It feels hacky, but it should work.

While you can use Pow.Operations.create/2, it makes more sense to just call the context module:

defmodule MyAppWeb.API.V1.RegistrationController do
  use MyAppWeb, :controller

  alias Ecto.Changeset
  alias Plug.Conn
  alias MyAppWeb.ErrorHelpers

  @spec create(Conn.t(), map()) :: Conn.t()
  def create(conn, %{"user" => user_params}) do
    user_params
    |> Pow.Ecto.Context.create(repo: MyApp.Repo, user: MyApp.Users.User)
    |> case do
      {:ok, _user} ->
        send_confirmation_email()
        json(conn, %{message: "Please check your email"}

      {:error, changeset} ->
        errors = Changeset.traverse_errors(changeset, &ErrorHelpers.translate_error/1)

        conn
        |> put_status(500)
        |> json(%{error: %{status: 500, message: "Couldn't create user", errors: errors}})
    end
  end

  # ...
end

For your use case this would probably be the best. But in the PowEmailConfirmation extension I just delete the session in the controller callback with Pow.Plug.delete/1.

2 Likes

Thanks for replying!

This makes much more sense, why reach into plug when what I really want is the context.

1 Like

Hi!,

One more question, the API guide says to use PowPersistentSession.Store.PersistentSessionCache in APIAuthPlug to keep the renew_token, but it doesn’t seem to be working anymore? Also, that stuff’s no longer in the docs for 1.0.18.

Should I writing a custom plug using PowPersistentSession.Plug.Base? I don’t think it’s possible to use the Cookie plug for an API.

Couldn’t edit the previous post.

I ran mix deps.clean and mix deps.get and that seems to have fixed PersistentSessioCache problem. Earlier keys weren’t being persisted, but they’re being persisted correctly now. Not sure what that problem was.

1 Like

Hi, I’m thinking of building an api using Phoenix Framework for the mobile app, the front end will be written in dart, with the help of flutter framework.
The communication between server and client will be made with Phoenix channels.
I wanted to ask how well does the Pow library play for such cases, it seems more geared to web apps usage, what does is take to make it work for mobile apps backends, did anyone use it similar way, is there a manual, tutorial or github repo to quickly look at the code?
A little example here would be nice too.

There’s a guide in the documentation on how to use Pow with an API

3 Likes

Thanks, doorgan, I’ve updated my comment, I ment to use Phoenix channels to make connection to the server, does this page cover this case?

I know there are many who have implemented Pow in mobile apps using e.g. Phoenix Channels. The way you need to implement it depends on your particular auth story though.

I do plan eventually to write some documentation for it, but for now there’s this gigantic issue dealing with Phoenix LiveView in particular: https://github.com/danschultzer/pow/issues/271

There’s also an older issue that might be of interest to you: https://github.com/danschultzer/pow/issues/153#issuecomment-478251934

But in short; you can use the API guide that @dorgan linked to create & authenticate the user. Then you can use the auth token to authorize access in your user socket, which would be almost the same as the logic you see in fetch/2 of the API auth plug.

1 Like

Thanks a lot, @danschultzer, I’ll dive straight into this!
And yes, I think most common usecases, such as authentication for html app, rest api (aka json api), websocket connections (channels, LiveView, etc) all need to have implementation examples in the docs, I see this is already the case for html and api usage.

Can Pow support also Mnesia for the other parts where Ecto is used for persistence?

I ma asking because I am building a project using only Mnesia as the database.

Yeah, but it depends how you wish to handle distribution.

You may want to run the MnesiaCache on a separate node, or use a custom MnesiaCache module, since MnesiaCache would override local data with what is in the cluster when joining. You can find more in the docs here: Pow.Store.Backend.MnesiaCache — Pow v1.0.19

1 Like

I think you misunderstood my question or I am misunderstanding your reply and linked docs.

In my understating Mnesia is used by Pow to persist sessions, and what I am asking is if Pow supports to use Mnesia also to persist the created users, aka when you register for the fist time, because I don’t see this mention anywhere, not even in the linked docs, but I may had misunderstood something.

By the way congrats for the awesome work you have done with Pow :slight_smile:

afaik Pow uses ecto - so you can try using patatoid / ecto3_mnesia · GitLab - you would need to replicate the Pow ecto migrations into a script creating the db tables as per the ecto3_mnesia readme…

1 Like