|
| 1 | +--- |
| 2 | +layout: guide |
| 3 | +title: "Resend email confirmation link" |
| 4 | +date: 2020-03-07 14:23:00 -0700 |
| 5 | +author: Dan Schultzer |
| 6 | +--- |
| 7 | + |
| 8 | +You may wish to add a link to the account edit page so users can manually request the confirmation e-mail to be send again in case they didn't receive it the first time. |
| 9 | + |
| 10 | +First add a controller with an action to resend the confirmation e-mail. Create `WEB_PATH/controllers/registration_controller.ex`: |
| 11 | + |
| 12 | +```elixir |
| 13 | +defmodule MyAppWeb.RegistrationController do |
| 14 | + use MyAppWeb, :controller |
| 15 | + |
| 16 | + def resend_confirmation_email(conn, _params) do |
| 17 | + case PowEmailConfirmation.Plug.pending_email_change?(conn) do |
| 18 | + true -> |
| 19 | + send_confirmation_email(conn) |
| 20 | + |
| 21 | + conn |
| 22 | + |> put_flash(:info, "E-mail sent, please check your inbox.") |
| 23 | + |> redirect(to: Routes.pow_registration_path(conn, :edit)) |
| 24 | + |
| 25 | + false -> |
| 26 | + conn |
| 27 | + |> put_flash(:info, "E-mail has already been confirmed.") |
| 28 | + |> redirect(to: Routes.pow_registration_path(conn, :edit)) |
| 29 | + end |
| 30 | + end |
| 31 | + |
| 32 | + defp send_confirmation_email(conn) do |
| 33 | + user = Pow.Plug.current_user(conn) |
| 34 | + |
| 35 | + PowEmailConfirmation.Phoenix.ControllerCallbacks.send_confirmation_email(user, conn) |
| 36 | + end |
| 37 | +end |
| 38 | +``` |
| 39 | + |
| 40 | +Update `WEB_PATH/router.ex` with the route (and it should only accessible for authenticated users): |
| 41 | + |
| 42 | +```elixir |
| 43 | +defmodule MyAppWeb.Router do |
| 44 | + use MyAppWeb, :router |
| 45 | + use Pow.Phoenix.Router |
| 46 | + |
| 47 | + # ... pipelines |
| 48 | + |
| 49 | + pipeline :protected do |
| 50 | + plug Pow.Plug.RequireAuthenticated, |
| 51 | + error_handler: Pow.Phoenix.PlugErrorHandler |
| 52 | + end |
| 53 | + |
| 54 | + scope "/", MyAppWeb do |
| 55 | + pipe_through [:browser, :protected] |
| 56 | + |
| 57 | + post "/registration/send-confirmation-email", RegistrationController, :resend_confirmation_email |
| 58 | + end |
| 59 | + |
| 60 | + # ... |
| 61 | +end |
| 62 | +``` |
| 63 | + |
| 64 | +Add the following section to your `WEB_PATH/templates/pow/registration/edit.html.eex` template (you may need to generate the templates first) after the `pow_user_id_field` field: |
| 65 | + |
| 66 | +```elixir |
| 67 | +<%= if @changeset.data.unconfirmed_email do %> |
| 68 | + <div> |
| 69 | + <p>Click the link in the confirmation email to change your email to <%= content_tag(:span, @changeset.data.unconfirmed_email) %>. Still haven't received the email? <%= link("Click here to send again", to: Routes.registration_path(@conn, :resend_confirmation_email), method: :post) %>.</p> |
| 70 | + </div> |
| 71 | +<% end %> |
| 72 | +``` |
| 73 | +
|
| 74 | +That's it! |
| 75 | + |
| 76 | +## Security |
| 77 | + |
| 78 | +As mentioned in the [production checklist](https://hexdocs.pm/pow/production_checklist.html#optional-rate-limit-e-mail-delivery), you should consider adding rate limitation to e-mail delivery. Otherwise the platform may be vulnerable to resource usage attacks. |
| 79 | + |
| 80 | +## Controller test |
| 81 | + |
| 82 | +```elixir |
| 83 | +defmodule MyAppWeb.RegistrationControllerTest do |
| 84 | + use MyAppWeb.ConnCase |
| 85 | + |
| 86 | + alias MyApp.{Users.User, Repo} |
| 87 | + |
| 88 | + setup %{conn: conn} do |
| 89 | + {:ok, user} = |
| 90 | + Pow.Ecto.Context.create(%{ |
| 91 | + |
| 92 | + password: "secret1234", |
| 93 | + password_confirmation: "secret1234" |
| 94 | + }, repo: Repo, user: User) |
| 95 | + |
| 96 | + conn = Pow.Plug.assign_current_user(conn, user, otp_app: :my_app) |
| 97 | + |
| 98 | + {:ok, conn: conn, user: user} |
| 99 | + end |
| 100 | + |
| 101 | + describe "resend_confirmation_email/2" do |
| 102 | + test "sends confirmation email", %{conn: conn} do |
| 103 | + conn = post conn, Routes.registration_path(conn, :resend_confirmation_email) |
| 104 | + |
| 105 | + assert redirected_to(conn) == Routes.pow_registration_path(conn, :edit) |
| 106 | + assert get_flash(conn, :info) == "E-mail sent, please check your inbox." |
| 107 | + end |
| 108 | + |
| 109 | + test "with already confirmed email", %{conn: conn, user: user} do |
| 110 | + user = PowEmailConfirmation.Ecto.Context.confirm_email(user, otp_app: :my_app) |
| 111 | + |
| 112 | + conn = |
| 113 | + conn |
| 114 | + |> Pow.Plug.assign_current_user(user, otp_app: :my_app) |
| 115 | + |> post(Routes.registration_path(conn, :resend_confirmation_email)) |
| 116 | + |
| 117 | + assert redirected_to(conn) == Routes.pow_registration_path(conn, :edit) |
| 118 | + assert get_flash(conn, :info) == "E-mail has already been confirmed." |
| 119 | + end |
| 120 | + end |
| 121 | +end |
| 122 | +``` |
0 commit comments