Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Advice] Need to add an attribute to new users from session #244

Open
coladarci opened this issue Sep 21, 2023 · 3 comments
Open

[Advice] Need to add an attribute to new users from session #244

coladarci opened this issue Sep 21, 2023 · 3 comments

Comments

@coladarci
Copy link

We have had a need surface where, when a new user is created via Pow Assent, I have to somehow hook into the creation process to pull something from the session and add to the user attributes.

Given the large number of "hooks" you have in place, there's a good chance this is possible but I can't seem to find any that would do what I need. Am I missing something? Or another clever solution?

Thanks for any advice!

@danschultzer
Copy link
Collaborator

FWIW there's a related question to this in Pow where a plug is sufficient to append params: pow-auth/pow#702 (comment)

For PowAssent it's different since we pull the user params from the provider response. There's no easy way of injecting it as the callback is called and then the response is processed all the way to creating the user:

def callback_upsert(conn, provider, params, redirect_uri) do
conn
|> callback(provider, params, redirect_uri)
|> handle_callback()
|> maybe_authenticate()
|> maybe_upsert_user_identity()
|> maybe_create_user()
|> case do
%{private: %{pow_assent_callback_state: {:ok, _method}}} = conn ->
{:ok, conn}
conn ->
{:error, conn}
end
end

First approach will be to inject it in the schema changeset or context, or higher level setting up a custom strategy where you modify the callback response. But neither gives access to the conn.

A more clever way would be to use process dictionary to hack it:

defmodule MyAppWeb.PowAssent.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: PowAssent.Phoenix.AuthorizationController, phoenix_action: :callback}} = conn, _opts) do
    put_pow_assent_user_params(conn)

    conn
  end

  def call(%{private: %{phoenix_controller: PowAssent.Phoenix.RegistrationController, phoenix_action: :create}} = conn, _opts) do
    put_pow_assent_user_params(conn)

    conn
  end

  def call(conn, _opts), do: conn

  defp put_pow_assent_user_params(conn) do
    # ...

    Process.put(:pow_assent_user_params, params)
  end
end
defmodule MyApp.Users.User do
  use Ecto.Schema
  use Pow.Ecto.Schema
  use PowAssent.Ecto.Schema

  # ...

  def changeset(user_or_changeset, attrs) do
    user_or_changeset
    |> pow_changeset(attrs)
    |> inject_attributes()
  end

  defp inject_attributes(changeset) do
    case Process.get(:pow_assent_user_params) do
      nil -> changeset
      attributes -> # ...
    end
  end
end

I'll think about how to make it simple to hook into params transformation though. The above should work, and maybe is a reasonable approach, but it would be nice to have full control over the transformation where it actually happens.

@coladarci
Copy link
Author

The only solution I came up with is

  1. DB Sets a temporary garbage value on create
  2. We redirect to a custom route on reg
  3. Process the session THERE and update record
  4. Redirect to wherever we normally would have redirected to
    Not awful but would love it to be less hacky if there's a way

@danschultzer
Copy link
Collaborator

Yeah, I think I'll add a way to be able to transform the conn right after provider callback has been processed. You can already set callbacks for when a new session is created (you can set them with PowAssent.Plug.put_create_session_callback/2), which is used do for the reauthorization plug. I think something similar should exist so you can transform the conn (with user params assign) during provider callback. This should make it possible to do anything with the user params.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants