From 2d5914c9704b711f15e3ed4a63e8fb7efe0c203f Mon Sep 17 00:00:00 2001 From: qdust41 Date: Wed, 8 Apr 2026 04:34:16 -0400 Subject: [PATCH] some not working changes trying to fix user login to include a username --- lib/mixer/accounts/user.ex | 6 +- lib/mixer_web/auth_overrides.ex | 2 +- lib/mixer_web/components/auth_components.ex | 20 ++-- lib/mixer_web/live/magic_sign_in_live.ex | 121 ++++++++------------ 4 files changed, 66 insertions(+), 83 deletions(-) diff --git a/lib/mixer/accounts/user.ex b/lib/mixer/accounts/user.ex index d5ba7ce..f5e0c29 100644 --- a/lib/mixer/accounts/user.ex +++ b/lib/mixer/accounts/user.ex @@ -434,6 +434,10 @@ defmodule Mixer.Accounts.User do identities do identity :unique_email, [:email] - identity :unique_username, [:username], nils_distinct?: true + identity :unique_username, [:username] do + eager_check_with Mixer.Accounts + message "is already taken" + nils_distinct? true + end end end diff --git a/lib/mixer_web/auth_overrides.ex b/lib/mixer_web/auth_overrides.ex index aafea61..5e0eb63 100644 --- a/lib/mixer_web/auth_overrides.ex +++ b/lib/mixer_web/auth_overrides.ex @@ -18,6 +18,6 @@ defmodule MixerWeb.AuthOverrides do # Inject the username field into the password registration form override AshAuthentication.Phoenix.Components.Password do - set :register_extra_component, &MixerWeb.AuthComponents.username_register_field/1 + set :register_extra_component, &MixerWeb.AuthComponents.username_field/1 end end diff --git a/lib/mixer_web/components/auth_components.ex b/lib/mixer_web/components/auth_components.ex index dbc93b1..9629ba7 100644 --- a/lib/mixer_web/components/auth_components.ex +++ b/lib/mixer_web/components/auth_components.ex @@ -11,7 +11,7 @@ defmodule MixerWeb.AuthComponents do Receives `form` (an `AshPhoenix.Form`) as an assign via the `register_extra_component` override. """ - def username_register_field(assigns) do + def username_field(assigns) do field = assigns.form[:username] assigns = @@ -27,7 +27,7 @@ defmodule MixerWeb.AuthComponents do Username
- @ + @
-

- {@field_errors |> List.first() |> elem(0)} -

-

- 3–30 characters · letters, numbers, underscores +

+ {translate_error(error)}

""" end + + def translate_error({msg, opts}) do + if count = opts[:count] do + Gettext.dngettext(MixerWeb.Gettext, "errors", msg, msg, count, opts) + else + Gettext.dgettext(MixerWeb.Gettext, "errors", msg, opts) + end + end end diff --git a/lib/mixer_web/live/magic_sign_in_live.ex b/lib/mixer_web/live/magic_sign_in_live.ex index ebc0b46..a9d925f 100644 --- a/lib/mixer_web/live/magic_sign_in_live.ex +++ b/lib/mixer_web/live/magic_sign_in_live.ex @@ -37,7 +37,7 @@ defmodule MixerWeb.MagicSignInLive do domain = Info.authentication_domain!(resource) # Determine whether this user needs to pick a username - needs_username? = needs_username?(token, resource) + needs_username? = needs_username?(token, resource, domain) form = resource @@ -75,70 +75,26 @@ defmodule MixerWeb.MagicSignInLive do @spec render(Socket.assigns()) :: Rendered.t() def render(assigns) do ~H""" -
- <.live_component - module={AshAuthentication.Phoenix.Components.Banner} - id="magic-sign-in-banner" - overrides={@overrides} - /> +
+
+ <.live_component + module={AshAuthentication.Phoenix.Components.Banner} + id="magic-sign-in-banner" + overrides={@overrides} + /> +
+ +
+ <.form :let={form} for={@form} phx-change="validate" phx-submit="submit" phx-trigger-action={@trigger_action} + action={auth_path(@socket, @subject_name, @auth_routes_prefix, @strategy, :sign_in)} + method="POST"> -
- <.form - :let={form} - for={@form} - phx-submit="submit" - phx-trigger-action={@trigger_action} - action={ - auth_path( - @socket, - @subject_name, - @auth_routes_prefix, - @strategy, - :sign_in - ) - } - method="POST" - > {hidden_input(form, :token, [])} - <%!-- Username field — only shown for new or username-less users --%> -
- -
- - @ - - -
-

- {form[:username].errors |> List.first() |> elem(0)} -

-

- 3–30 characters · letters, numbers, underscores -

-
+ <%!-- Using the unified component --%> + - {submit("Sign in", - class: "btn btn-primary w-full mt-2", - phx_disable_with: "Signing in…" - )} + {submit("Sign in", class: "btn btn-primary w-full", phx_disable_with: "Signing in...")}
@@ -156,28 +112,43 @@ defmodule MixerWeb.MagicSignInLive do form_params = Map.get(params, subject_name, %{}) - form = Form.validate(socket.assigns.form, form_params) + # Use Form.validate with :all_errors to surface uniqueness constraints + form = + socket.assigns.form + |> Form.validate(form_params, errors: true) - socket = - socket - |> assign(:form, form) - |> assign(:trigger_action, form.valid?) + if form.valid? do + # Only trigger the POST redirect if the data is truly valid + {:noreply, assign(socket, form: form, trigger_action: true)} + else + socket = + socket + |> assign(form: form, trigger_action: false) + {:noreply, socket} + end + end - {:noreply, socket} + @impl true + def handle_event("validate", params, socket) do + subject_name = socket.assigns.subject_name |> to_string() |> slugify() + form_params = Map.get(params, subject_name, %{}) + + form = Form.validate(socket.assigns.form, form_params, errors: true) + {:noreply, assign(socket, form: form)} end # ── Helpers ────────────────────────────────────────────────────────────────── # Returns true if the user is new or has no username set yet. - defp needs_username?(nil, _resource), do: true + defp needs_username?(nil, _resource, _domain), do: true - defp needs_username?(token, resource) do + defp needs_username?(token, resource, domain) do with {:ok, claims} <- AshAuthentication.Jwt.peek(token), # 1. Try to find an existing user from the claims - user <- find_user(claims, resource), + user <- find_user(claims, resource, domain), # 2. If a user exists, check if they already have a username false <- is_nil(user) do - is_nil(user.username) + is_nil(user.username) or user.username == "" else _ -> # Unknown / new user — ask for username to be safe @@ -185,7 +156,7 @@ defmodule MixerWeb.MagicSignInLive do end end - defp find_user(claims, resource) do + defp find_user(claims, resource, domain) do # Try 'sub' first if it looks like a user subject (e.g. "User:123") sub = Map.get(claims, "sub") @@ -201,7 +172,11 @@ defmodule MixerWeb.MagicSignInLive do user || case Map.get(claims, "identity") || Map.get(claims, "email") do email when is_binary(email) -> - case Ash.get(resource, [email: email], action: :get_by_email, authorize?: false) do + # Use for_read with the explicit action and arguments + resource + |> Ash.Query.for_read(:get_by_email, %{email: email}) + |> Ash.read_one(domain: domain, authorize?: false) + |> case do {:ok, user} -> user _ -> nil end