CuratorConfirmable

Adds an email confirmation workflow to Curator.

NOTE: You’ll need to configure an email adapter.

Installation

  1. Add curator_confirmable to your list of dependencies in mix.exs:

    def deps do
      [{:curator_confirmable, "~> 0.1.0"}]
    end
  2. Run the install command

    mix curator_confirmable.install
  3. Update web/models/user.ex

    defmodule Auth.User do
      use Auth.Web, :model
    
      use CuratorConfirmable.Schema
    
      schema "users" do
        ...
        curator_confirmable_schema
        ...
      end
    end
  4. Update web/router.ex

    scope "/", Auth do
      pipe_through [:browser]
    
      resources "/confirmations", ConfirmationController, only: [:new, :create, :edit]
    
      ...
    end
    
    pipeline :browser do
      ...
    
      plug Curator.Plug.LoadSession
      ...
      plug CuratorConfirmable.Plug
      ...
      plug Curator.Plug.EnsureResourceOrNoSession, handler: <YourApp>.ErrorHandler
    end
    
    pipeline :authenticated_browser do
      ...
    
      plug Curator.Plug.LoadSession
      ...
      plug CuratorConfirmable.Plug
      ...
      plug Curator.Plug.EnsureResourceAndSession, handler: <YourApp>.ErrorHandler
    end
  5. Update lib/<otp_app>/curator_hooks.ex

    def after_extension(conn, :registration, user) do
      conn
      |> put_flash(:info, "Account was successfully created. Check your email for a confirmation link")
      |> send_email(:confirmation, user)
      |> redirect(to: "/")
    end
    
    def after_extension(conn, :confirmation, _user) do
      conn
      |> put_flash(:info, "Account confirmed.")
      |> redirect(to: "/")
    end
    
    def send_email(conn, :confirmation, user) do
      {user, token} = CuratorConfirmable.set_confirmation_token!(user)
    
      url = confirmation_url(conn, :edit, token)
    
      # NOTE: Insert a call to your email library
      PhoenixCurator.Email.confirmation_email(user, url)
      |> PhoenixCurator.Mailer.deliver_now
    
      conn
    end

    NOTE: The email can be setup however you wish. If using Bamboo it could look something like this:

    def confirmation_email(%{email: email}, url) do
      new_email
      |> to(email)
      |> from(@sender)
      |> subject("Welcome #{email}.")
      |> html_body("<strong>Click <a href=\"#{url}\">HERE</a> to confirm you account</strong>")
      |> text_body("visit the following URL to confirm you account: #{url}")
    end
  6. Update test/supprt/session_helper.ex

    def create_user(user, attrs) do
      user
      |> PhoenixCurator.User.changeset(attrs)
      |> PhoenixCurator.User.password_changeset(%{password: "TEST_PASSWORD", password_confirmation: "TEST_PASSWORD"})
      |> Ecto.Changeset.change(confirmed_at: Timex.now)
      ...
      |> PhoenixCurator.Repo.insert!
    end
  7. Update test/controllers/page_controller_test.exs

    test "visiting a secret page w/ a signed_in unconfirmed user", %{conn: conn} do
      user = User.changeset(%User{}, @user_attrs)
      |> User.password_changeset(%{password: "TEST_PASSWORD", password_confirmation: "TEST_PASSWORD"})
      #|> Ecto.Changeset.change(confirmed_at: Ecto.DateTime.utc)
      #|> User.approvable_changeset(%{approval_at: Ecto.DateTime.utc, approval_status: "approved", approver_id: 0})
      |> Auth.Repo.insert!
    
      conn = conn
      |> sign_in(user)
      |> get("/secret")
    
      assert Phoenix.Controller.get_flash(conn, :danger) == "Not Confirmed"
      assert Phoenix.ConnTest.redirected_to(conn) == session_path(conn, :new)
    end