How to create email confirmation for user accounts

Hi folks,

Beginner with elixir & AshAuthentication, trying to extend our app with functionality to email a new user with a link for them to confirm their ownership of the email address, and block them from signing in before their email address is confirmed. Seems like we need two routes:

  1. Sends un-confirmed users to a dummy page directing them to confirm their email address via the email (potentially also including an input field for them to enter their email address so they can be re-sent the confirmation email)

  2. Handles inbound confirmation email link clicks.

We have a sender working to send the email to the user upon registration with embedded URI in the form app/auth/user/confirm/<token>.

Also able to manually complete confirmation with the following (per docs) so that the confirmed_at field in the database is populated with a datetime.

strategy = AshAuthentication.Info.strategy!(MyApp.Accounts.User, :confirm)
{:ok, user} = AshAuthentication.Strategy.action(strategy, :confirm, %{"confirm" => “<token>”})

Next step seems to be to create the routes above, unless there are some in-built endpoints that we’re missing?

Anyone able to provide guidance would be greatly appreciated.



@jimsynz would need to confirm, but I’m pretty sure that there is a confirmation route that ships with ash_authentication that you can use.

As for redirecting to a page to let them know they need to confirm you can do that in the AuthController you likely built as part of following the setup instructions for ash_authentication_phoenix (AFAIK there is no built-in route for this):

  def success(conn, _activity, user, _token) do
    return_to = get_session(conn, :return_to) || ~p"/"
  if user.confirmed_at do
    |> delete_session(:return_to)
    |> store_in_session(user)
    |> assign(:current_user, user)
    |> redirect(to: return_to)
    |> put_flash(:error, "You must confirm your email address.")
    |> redirect(to: ...)

The predefined route helpers become available for use when you use:

use AshAuthentication.Phoenix.Router

You can see the source of that file here: ash_authentication_phoenix/lib/ash_authentication_phoenix/router.ex at d82b33fe0a9b649e0a65424309f505e82776bd42 · team-alembic/ash_authentication_phoenix · GitHub

There are a lot of macros so it can be a bit daunting for a beginner, they still are for me. I don’t see a confirm route there, but I may be missing something.


Looks like some routes are created here dynamically based on strategies set on your User resource (or whatever resource you are using for Authentication)

 scope path, scope_opts do
        for strategy <- strategies do
          for {path, phase} <- AshAuthentication.Strategy.routes(strategy) do
            match :*,
                  {subject_name,, phase},
                  as: :auth,
                  private: %{strategy: strategy}

So we you need to see what routes your strategy defines if any.

That is defined here: ash_authentication/lib/ash_authentication/add_ons/confirmation/strategy.ex at 71d510efc6acb94771ed2d0f199f6799e2881c77 · team-alembic/ash_authentication · GitHub

 @doc false
  @spec routes(Confirmation.t()) :: [Strategy.route()]
  def routes(strategy) do
    subject_name = Info.authentication_subject_name!(strategy.resource)

    path =
      |> Path.join()

    [{"/#{path}", :confirm}]

So if I’m following this right, you need to use this router helper:

auth_routes_for Example.Accounts.User, to: AuthController

And have the strategy defined on User and it should dynamically inject that confirm route in there. Try putting that in your router then running: mix phx.routes and see what comes up.

1 Like