pow-auth / pow

Robust, modular, and extendable user authentication system
https://powauth.com
MIT License
1.58k stars 152 forks source link

Inject values on registration #702

Closed an0h closed 12 months ago

an0h commented 12 months ago

hello i am just starting trying to implement pow and its slick, but i am unable to express in my elixir knowledge how to correctly use the before_process to inject a value into the conn and have it saved to the db, i get the conn updated to show the user object i desire but my injected values never save to the db.

the fields both work to save values if i input them in the form... just i have something i want to do autmoated wise reeal quick before i insert the record and i thought overwitint the conn is the way to do it, i tried a few ways to do this, is it possible or is there a more Elixir expressive way to update the record before inserting it?

This is my unsuccessful attempt, below:

  def before_process(Pow.Phoenix.RegistrationController, :create, conn, config) do
    IO.puts "beforeprocess"
    IO.inspect conn
    # ^ this conn here shows original form values
    %{params: %{"user" => user}} = conn
    with %{foo: foo, bar: bar} <- MyAppAssigner.find_user_unique(user) do
      conn = %{conn | body_params: %{conn.body_params | "user" => foo}}
      conn = %{conn | params: %{conn.params | "user" => foo}}
      IO.puts "i have modified conn"
      IO.inspect conn
      # ^ this conn shows desired user object in both params fields modified above, and then i return it?
      conn
    else
      e ->
        IO.inspect e
        conn
    end
  end
danschultzer commented 12 months ago

The controller callbacks (which uses before_process) are used in extensions, so you'll need to set this up as an extension.

There's a simpler approach using a plug:

defmodule MyAppWeb.Pow.InjectRegistrationPlug do
  @moduledoc """
  This plug injects attributes during user registration.

  ## Example

      plug MyAppWeb.Pow.InjectRegistrationPlug
  """

  def init(opts), do: opts

  def call(%{private: %{phoenix_controller: Pow.Phoenix.RegistrationController, phoenix_action: :new}} = conn, _opts) do
    case MyAppAssigner.find_user_unique(conn.params["user"]) do
      %{foo: foo, bar: bar} -> %{conn | params: %{conn.params | "user" => foo}}
      _else -> conn
    end
  end
end

Then you add this plug to the router.

Alternatively you could handle this in the context module:

defmodule MyApp.Users do
  use Pow.Ecto.Context,
    repo: MyApp.Repo,
    user: MyApp.Users.User

  def create(params) do
    params
    |> unique_user_params()
    |> pow_create(params)
  end

  defp unique_user_params(params) do
    case MyAppAssigner.find_user_unique(params) do
      %{foo: foo, bar: bar} -> foo
      nil -> params
    end
  end
end

In this case you need to update the config with users_context: MyApp.Users for it to pick up the custom context.

an0h commented 12 months ago

Thanks so very much for the elegant Elixir here on this issue, and most especially in this module!