defmodule MixerWeb.MagicSignInLive do @moduledoc """ Custom magic-link sign-in LiveView that collects a username for new users. When a user clicks their magic link, this page is shown instead of the default auto-submit. If the user is brand new (no account) or has no username set yet, we ask them to choose one before completing sign-in. """ use AshAuthentication.Phoenix.Overrides.Overridable, root_class: "CSS class for the root `div` element.", magic_sign_in_id: "Element ID for the `MagicSignIn` LiveComponent." use AshAuthentication.Phoenix.Web, :live_view alias AshAuthentication.Info alias AshPhoenix.Form alias Phoenix.LiveView.{Rendered, Socket} import AshAuthentication.Phoenix.Components.Helpers, only: [auth_path: 5] import PhoenixHTMLHelpers.Form, only: [hidden_input: 3, submit: 2] import Slug @doc false @impl true def mount(params, session, socket) do overrides = session |> Map.get("overrides", [AshAuthentication.Phoenix.Overrides.Default]) resource = session["resource"] strategy_name = session["strategy"] token = params["token"] || params["magic_link"] strategy = Info.strategy!(resource, strategy_name) subject_name = Info.authentication_subject_name!(resource) domain = Info.authentication_domain!(resource) # Determine whether this user needs to pick a username needs_username? = needs_username?(token, resource) form = resource |> Form.for_action(strategy.sign_in_action_name, params: %{"token" => token}, domain: domain, as: subject_name |> to_string(), id: "#{subject_name}-#{strategy_name}-sign-in-form" |> slugify(), context: %{strategy: strategy, private: %{ash_authentication?: true}} ) socket = socket |> assign(overrides: overrides) |> assign(:token, token) |> assign(:strategy, strategy) |> assign(:subject_name, subject_name) |> assign(:resource, resource) |> assign(:needs_username?, needs_username?) |> assign(:form, form) |> assign(:trigger_action, false) |> assign(:current_tenant, session["tenant"]) |> assign(:auth_routes_prefix, session["auth_routes_prefix"]) {:ok, socket} end @doc false @impl true @spec handle_params(map, String.t(), Socket.t()) :: {:noreply, Socket.t()} def handle_params(_params, _uri, socket), do: {:noreply, socket} @doc false @impl true @spec render(Socket.assigns()) :: Rendered.t() def render(assigns) do ~H"""
{form[:username].errors |> List.first() |> elem(0)}
3–30 characters · letters, numbers, underscores