wemgl
Creating custom controller callbacks with Pow
I’m working on an app that uses Pow to handle user authentication. I was able to get it working by following the documentation, but I ran into an issue that I don’t see a clear solution for.
The application is for a subscription service where new users are given a free trial after signing up. I use Pow to create the user, which works fine. Now, I’m trying to associate the newly created user with a subscription using the controller callback feature from the Phoenix controllers section of the README. Here’s my implementation so far:
defmodule AddSubscription.Phoenix.ControllerCallbacks do
use Pow.Extension.Phoenix.ControllerCallbacks.Base
require Logger
@impl true
def before_respond(Pow.Phoenix.RegistrationController, :create, {:ok, user, conn}, _config) do
Logger.debug("user registration before_respond: #{inspect(user)}")
with {:ok, user} <- Trumpet.insert_subscription(%{user_id: user.id}) do
{:ok, user, conn}
end
end
end
I’ve also written a controller test to exercise the behavior works as intended:
defmodule Pow.Phoenix.RegistrationControllerTest do
use TrumpetWeb.ConnCase
describe "POST /registration" do
test "creates a user with a subscription", %{conn: conn} do
email = "wembley.gl@gmail.com"
post(
conn,
"/registration",
%{
user: %{
email: email,
password: "test1234567890",
confirm_password: "test1234567890",
}
}
)
user = Trumpet.get_user_by_email_with_subscription(email)
assert user.subscription
assert Trumpet.Subscription.trial?(user.subscription)
end
end
end
The user creation works as expected, but the controller callback isn’t firing, so the assertion for user.subscription fails. I’ve taken a look at the Extensions section for Pow, from here, and looked at the implementation for other extensions, like PowPasswordReset, and it looks like an extension should be implemented as a library application, is that right? Also, how do I configure Pow to register an extension once it’s created? I apologize if this is in the documentation and I’m missing it.
Marked As Solved
danschultzer
The callbacks is the wrong place to deal with this. You would want it to be inserted in a transaction so if the subscription insert fails, the user won’t be created. There’re multiple ways to deal with it, but if all users inserted needs to have a subscription created along it, then I would keep it simple and just deal with it in the user changeset:
defmodule MyApp.Users.User do
use Ecto.Schema
use Pow.Ecto.Schema
schema "users" do
has_one :subscription, MyApp.Users.Subscription
pow_user_fields()
timestamps()
end
def changeset(user_or_changeset, attrs) do
user
|> pow_changeset(attrs)
|> maybe_add_subscription()
end
defp maybe_add_subscription(changeset) do
case Ecto.get_meta(changeset.data, :state) do
:built -> add_subscription(changeset)
:loaded -> changeset
end
end
defp add_subscription(changeset) do
Ecto.Changeset.cast_assoc(changeset, :subscription, with: &MyApp.Users.Subscription.changeset/2)
end
end
If you need more control over the flow, then I would either set up a custom controller and/or custom context module.
(Also, as you guessed the controller callbacks module you’ve set up is only for custom extensions)
Also Liked
wemgl
Thanks for the quick response. You make a valid point. I was caught up trying to leverage the functionality in Pow, that I forgot that I was able to make modifications to the User changeset directly, since all I did to add the Pow support was add pow_user_fields and the various use macros to bring in functions.
danschultzer
There is a duplicate thread that I answered this in: POW: Custom controller doubts - #2 by danschultzer
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #javascript
- #code-sync
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








