Giter Site home page Giter Site logo

Comments (11)

abartier avatar abartier commented on May 3, 2024 1

Perfect! I'll try that later today or tomorrow.
As mentioned I'll try to make a PR for the guides to cover this part.

The invitation extension could be great but I never used this approach before.
In our main saas app this is what happens: client request -> admin setup an account with the first user -> client can add the other users of his team. So basically every step is protected (need to be logged). That's what I'm trying to do as an exercise for pow. What is interesting here is that we need few different changesets and render actions depending on who calls the action.

I like the fact that extracting Pow.Plug is quite easy.
Authentication is just a series of Plug (encrypt pass, check pass, validate token, ....), it seems that Pow allow this approach (Pow is just an engine exposing plugs as an interface).

I'll close this once the experience is finished and the guide completed ;)

from pow.

abartier avatar abartier commented on May 3, 2024 1

Ok, perfect.
I think that I have all the information to finish the guide.
I'll PR soon.
Thanks!

from pow.

danschultzer avatar danschultzer commented on May 3, 2024

I've been working on a guide for invitation/closed registration setup myself, and I think I'll create an extension for it. There's been a lot of interest for invitation setup, and it's probably not trivial for people to implement.

From the top of my head this is what you would need:

  • Add a hidden input box with the invitation token value to the registration form
  • Add changeset validation for invitation token

And for most cases you would also need:

  • Create a custom controller for handling invitation registrations
  • Create a plug action to validate invitation tokens

This is the basis of dynamic invitation registrations. I'll see if I can build the base for an invitation extension soon. It would make it a lot easier.

As for the custom registration controller, I would set keep it simple like this:

defmodule MyAppWeb.RegistrationController do
  use MyAppWeb, :controller

  alias Pow.Phoenix.RegistrationController, as: PowRegistrationController

  def new(conn, params), do: PowRegistrationController.new(conn, params)

  def create(conn, params), do: PowRegistrationController.create(conn, params)

  def edit(conn, params), do: PowRegistrationController.edit(conn, params)

  def update(conn, params), do: PowRegistrationController.update(conn, params)

  def delete(conn, params), do: PowRegistrationController.delete(conn, params)
end

Later, if necessary, it can be expanded by extracting the actual plug actions:

def new(conn, _params) do
  changeset = Pow.Plug.change_user(conn)

  conn
  |> assign(:changeset, changeset)
  |> render("new.html")
end

Remember that the views and templates has to be renamed, and moved. You would need to make your own routes too instead of using pow_routes.

Nobody have made custom registration controllers before so there's probably a bunch of caveats that I haven't thought about, and there may be things in pow that should be changed to make it easier to work with.

If you're willing to try it out, please let me know about your experience as the option for this kind of customization is a vital part of Pow 😄

from pow.

abartier avatar abartier commented on May 3, 2024

I'm working on the session part and I can't login.

Considering this code:

  # Session controller

  use PowtestWeb, :controller

  def new(conn, _params) do
    changeset = Pow.Plug.change_user(conn)

    conn
    |> render("login.html", changeset: changeset)
  end

  def create(conn, params) do
    case Pow.Plug.authenticate_user(conn, params) do
      {:ok, conn} ->
        conn
        |> put_flash(:info, "Welcome back")
        |> redirect(to: Routes.page_path(conn, :private_page))
      {:error, conn} ->
        conn
        |> put_flash(:info, "Email or Password incorect")
        |> render( "login.html", changeset: conn.assign.changeset )
    end
  end

  # Routes

  scope "/", PowtestWeb do
    pipe_through :browser

    get "/", PageController, :index
    get "/signup", AuthController, :new
    post "/signup", AuthController, :create, as: :signup
    get "/login", SessionsController, :new, as: :login
    post  "/login", SessionsController, :create, as: :login
    delete "/logout", SessionsController, :delete, as: :logout

  end

When trying to log, I've got this error: no function clause matching in String.downcase/2
nil seems to have been passed to the function, but the params got the right informations.

Any Idea why?

from pow.

danschultzer avatar danschultzer commented on May 3, 2024

I think this may be what's missing (the Pow.Plug.authenticate_user/2 expects a map with email and password):

  def create(conn, %{"user" => user_params}) do
    case Pow.Plug.authenticate_user(conn, user_params) do
      # ...
    end
  end

from pow.

abartier avatar abartier commented on May 3, 2024

Actually that was my initial approach but when doing this I've got:

KeyError at POST /login key :assign not found in

Reading the pow code I see that the pattern match occurs later and the function need the full params, but I'm maybe wrong.

from pow.

danschultzer avatar danschultzer commented on May 3, 2024

I think this bit is the problem:

|> render( "login.html", changeset: conn.assign.changeset )

It should be:

|> render( "login.html", changeset: conn.assigns.changeset )

from pow.

abartier avatar abartier commented on May 3, 2024

Thanks for the typo. But it didn't solve the error:

key :changeset not found in: %{current_user: nil}

I'm actually checking what Pow.Plug.authenticate_user is actually returning in case of an error.

edit: the login works if a legit user try the form.

from pow.

danschultzer avatar danschultzer commented on May 3, 2024

Yeah, there's no changeset automatically assigned in that call. You'll need to do this instead:

  def create(conn, %{"user" => user_params}) do
    case Pow.Plug.authenticate_user(conn, user_params) do
      {:ok, conn} ->
        conn
        |> put_flash(:info, "Welcome back")
        |> redirect(to: Routes.page_path(conn, :private_page))
      {:error, conn} ->
         changeset = Pow.Plug.change_user(conn, conn.params["user"])

        conn
        |> put_flash(:info, "Email or Password incorect")
        |> render( "login.html", changeset: changeset)
    end
  end

Here's the doc/specs: https://github.com/danschultzer/pow/blob/master/lib/pow/plug.ex#L72-L77

The plug actions will mostly just transform conn, and in authenticate_user it'll do nothing if an error occurs.

from pow.

danschultzer avatar danschultzer commented on May 3, 2024

Also, this is the controller action in Pow: https://github.com/danschultzer/pow/blob/master/lib/pow/phoenix/controllers/session_controller.ex#L32-L50

Pow has the action split into process_ACTION and response_ACTION, but should be easy to read since it usually only consists of one plug method call, and then just processing the results of that call.

from pow.

abartier avatar abartier commented on May 3, 2024

I just PR for the new guide. I'll keep improving it.
Maybe a ref to it in the installation guide could help?

from pow.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.