defmodule BerrypodWeb.Auth.TotpVerification do use BerrypodWeb, :live_view alias Berrypod.Accounts @impl true def mount(_params, session, socket) do user_id = session["totp_pending_user_id"] remember_me = session["totp_pending_remember_me"] if user_id do {:ok, socket |> assign(:user_id, user_id) |> assign(:remember_me, remember_me) |> assign(:form, to_form(%{"code" => ""}, as: :totp)) |> assign(:trigger_submit, false)} else {:ok, socket |> put_flash(:error, "Session expired. Please log in again.") |> redirect(to: ~p"/users/log-in")} end end @impl true def render(assigns) do ~H"""
<.header> Two-factor authentication <:subtitle> Enter the 6-digit code from your authenticator app.
<.form for={@form} id="totp_form" action={~p"/users/verify-totp"} phx-submit="verify" phx-trigger-action={@trigger_submit} > <.input field={@form[:code]} type="text" label="Verification code" placeholder="000000" inputmode="numeric" pattern="[0-9]*" autocomplete="one-time-code" maxlength="6" autofocus phx-mounted={JS.focus()} /> <.button variant="primary" class="admin-btn-block"> Verify
""" end @impl true def handle_event("verify", %{"totp" => %{"code" => code}}, socket) do user = Accounts.get_user!(socket.assigns.user_id) case Accounts.verify_totp(user, code) do :ok -> {:noreply, assign(socket, :trigger_submit, true)} :error -> {:noreply, socket |> assign(:form, to_form(%{"code" => ""}, as: :totp)) |> put_flash(:error, "Invalid code. Please try again.")} end end end