pow icon indicating copy to clipboard operation
pow copied to clipboard

Making `PowEmailConfirmation` play nice with custom controllers

Open samm81 opened this issue 4 years ago • 3 comments

Hi,

Thanks for all your hard work on this great library! I got user email confirmation up and running in just a couple of hours thanks to pow 😁

I wanted to share my experience, both for future users who might encounter my same issue, and as a feature suggestion.

I use a custom controller for registration with pow. When I was setting up PowEmailConfirmation I ran into an issue when a user clicked on the confirmation link that is sent after updating their email (though if I had been using a custom session controller as well, this issue would also appear when a user first confirms their email):

** (UndefinedFunctionError) function MyAppWeb.Router.Helpers.pow_registration_path/3 is undefined or private

This occurs because after PowEmailConfirmation.Phoenix.ConfirmationController finishes confirming the token, it calls redirect_to/1 (https://github.com/danschultzer/pow/blob/e4b97c3dffbe287e19bde74e2be029644a0e4038/lib/extensions/email_confirmation/phoenix/controllers/confirmation_controller.ex#L25), which in turn tries to call Router.Helpers.pow_registration_path/3 through a function routes(conn).registration_path/2 which I don't totally understand.

If you, like me, don't want to write a ConfirmationController yourself, you can fix this issue with an ugly hack in your Router:

resources "/user", RegistrationController, only: [:show, :edit, :update], singleton: true
# miserable hack to comply with `PowEmailConfirmation`s expectations
resources "/uuser", RegistrationController, only: [:edit], singleton: true, as: :pow_registration

This essential "redirects" :pow_registration_path to RegistrationController while also allowing you to keep using Routes.registration_path elsewhere in your application. (You'd do the same thing for SessionController :new if you had a custom SessionController.)

The downside is that after a user confirms their updated email, they'll find themselves at "myapp.com/uuser" instead of "myapp.com/user".

As a feature request: it would be great if we could somehow inform pow that we're using a custom controller for registration/session (or maybe there already is? I don't really understand routes(conn).registration_path...)

samm81 avatar Jul 10 '20 23:07 samm81

There's a much simpler way, as the registration_path/2 calls the callback routes module. You can create a custom routes module and then just use your registration path:

defmodule MyAppWeb.Pow.Routes do
  use Pow.Phoenix.Routes
  alias MyAppWeb.Router.Helpers, as: Routes

  def registration_path(conn, verb), do: Routes.registration_path(conn, verb)
end

An (uglier) alternative is to set use the pow alias in the scope for the registration routes:

resources "/user", RegistrationController, only: [:show, :edit, :update], singleton: true

scope "/", as: :pow do
  resources "/user", RegistrationController, only: [:show, :edit, :update], singleton: true
end

The docs could need an update regarding mix with extensions. Ideally custom controllers mean that you take full control over the flow though and don't use any the extensions either, but I do understand it's not really the best for all cases. I'm thinking of introducing mix tasks to produce custom controllers which will produce something akin to mix phx.gen.auth where you will be able to understand how it's all connected.

danschultzer avatar Jul 11 '20 00:07 danschultzer

"I'm thinking of introducing mix tasks to produce custom controllers which will produce something akin to mix phx.gen.auth where you will be able to understand how it's all connected." I think that would be awesome, I do hope that won't cause a lot of work. In fact you would be translating back to general code a lot of the stuff you got incorporated in Pow.

joepstender avatar Jul 11 '20 00:07 joepstender

In fact you would be translating back to general code a lot of the stuff you got incorporated in Pow.

Yeah, it's one of the things I'm trying to figure out. I would probably still leverage Pow quite a bit focusing on making it super easy customizing flow this way. phx.gen.auth introduces code injection in mix tasks, so I feel that now I can finally do with Pow what I originally wanted to do 😄 (back then there was no "official" library that did code injection so I assumed it was just a bad idea)

danschultzer avatar Jul 14 '20 03:07 danschultzer