Guy14

Guy14

Hello community!

I encounter a warning saying that AshAuthentication.Errors.AuthenticationFailed does not implement the AshPhoenix.FormData.Error protocol while running tests (with an voluntary wrong password) for a Phoenix Liveview.

...12:06:14.730 [warning] Unhandled error in form submission for Prepair.AshDomains.Accounts.User.change_password

This error was unhandled because AshAuthentication.Errors.AuthenticationFailed does not implement the `AshPhoenix.FormData.Error` protocol.

** (AshAuthentication.Errors.AuthenticationFailed) Authentication failed

I’ve read this issue: This error was unhandled because it did not implement the `AshPhoenix.FormData.Error` protocol. · Issue #516 · ash-project/ash · GitHub and tried the proposed solution, but it didn’t worked for me.

Here is my change_password action:

update :change_password do
      # Use this action to allow users to change their password by providing
      # their current password and a new password.

      require_atomic? false
      accept []
      argument :current_password, :string, sensitive?: true, allow_nil?: false

      argument :password, :string,
        sensitive?: true,
        allow_nil?: false

      argument :password_confirmation, :string,
        sensitive?: true,
        allow_nil?: false

      validate confirm(:password, :password_confirmation)

      validate {AshAuthentication.Strategy.Password.PasswordValidation,
                strategy_name: :password, password_argument: :current_password}

      change {AshAuthentication.Strategy.Password.HashPasswordChange,
              strategy_name: :password}
    end

I tried to add change set_context(%{strategy_name: :password}) before the other change call, but it didn’t solved the warning.

Here are the current uses and imports on my resource:

defmodule Prepair.AshDomains.Accounts.User do
  use Ash.Resource,
    domain: Prepair.AshDomains.Accounts,
    data_layer: AshPostgres.DataLayer,
    authorizers: [Ash.Policy.Authorizer],
    extensions: [AshAuthentication]

  alias Prepair.AshDomains.Profiles.Profile

  import Prepair.AshDomains.ValidationMacros

Here are the current uses and imports in my domain (which exposes the code interfaces then used in the liveview).

I wonder if I should add AshPhoenix there.

defmodule Prepair.AshDomains.Accounts do
  use Ash.Domain

And here is my handle_event function for the save action in the Liveview:

def handle_event("save", %{"user" => params}, socket) do
    current_user = socket.assigns.current_user

    form =
      AshPhoenix.Form.for_update(current_user, :change_password,
        domain: Accounts,
        as: "user"
      )
      |> AshPhoenix.Form.validate(params)

    case AshPhoenix.Form.submit(form, params: params) do
      {:ok, _user} ->
        {:noreply,
         socket
         |> put_flash(:info, "Password updated successfully")
         |> redirect(to: ~p"/")}

      {:error, form} ->
        {:noreply,
         socket |> assign(:form, to_form(form)) |> assign(check_errors: true)}
    end
  end

Showing Posts 1 to 5

FlyingNoodle

FlyingNoodle

  1. You need the pass the current_user as actor.
  2. You don’t need the call to validate since submit will validate your form anyway.
  3. I assume you assign you form to the socket in some other callback. In that case you can just do: AshPhoenix.Form.submit(socket.assigns.my_form_that_i_assigned, params: params, actor: current_user)
Guy14

Guy14 OP

Thanks for you reply @FlyingNoodle !

I’ve done the refactors you adviced, the test still passes (and my code is improved), but I still have the warning that AshAuthentication errors are not handled by AshPheonix.FormData.Error.

Here is my full current liveview:

defmodule PrepairWeb.UserUpdatePasswordLive do
  use PrepairWeb, :live_view
  use Gettext, backend: PrepairWeb.Gettext

  alias Prepair.AshDomains.Accounts

  def render(assigns) do
    ~H"""
    <div class="mx-auto max-w-sm">
      <.header class="text-center">
        <%= gettext("Update your password") %>

        <:actions>
          <.link patch={~p"/"}>
            <.button><%= gettext("Go Back Home") %></.button>
          </.link>
        </:actions>
      </.header>

      <.simple_form
        for={@update_password_form}
        id="update_password_form"
        phx-submit="save"
        phx-change="validate"
      >
        <.error :if={@check_errors}>
          <%= gettext("Oops, something went wrong! Please check the errors below.") %>
        </.error>

        <.input
          field={@update_password_form[:email]}
          type="hidden"
          id="hidden_user_email"
          value={@current_email}
        />

        <.input
          field={@update_password_form[:current_password]}
          type="password"
          label={gettext("Current password")}
          required
        />

        <.input
          field={@update_password_form[:password]}
          type="password"
          label={gettext("New password")}
          required
        />

        <.input
          field={@update_password_form[:password_confirmation]}
          type="password"
          label={gettext("New password confirmation")}
          required
        />

        <:actions>
          <.button phx-disable-with={gettext("Updating password...")} class="w-full">
            <%= gettext("Update password") %>
          </.button>
        </:actions>
      </.simple_form>
    </div>
    """
  end

  def mount(_params, _session, socket) do
    current_user = socket.assigns.current_user

    form =
      AshPhoenix.Form.for_update(current_user, :change_password,
        domain: Accounts,
        as: "user"
      )

    socket =
      socket
      |> assign(check_errors: false)
      |> assign(:current_email, current_user.email)
      |> assign(:update_password_form, to_form(form))

    {:ok, socket}
  end

  def handle_event("save", %{"user" => params}, socket) do
    current_user = socket.assigns.current_user
    form = socket.assigns.update_password_form

    case AshPhoenix.Form.submit(form,
           params: params,
           actor: current_user
         ) do
      {:ok, _user} ->
        {:noreply,
         socket
         |> put_flash(:info, "Password updated successfully")
         |> redirect(to: ~p"/")}

      {:error, form} ->
        {:noreply,
         socket
         |> assign(:update_password_form, to_form(form))
         |> assign(check_errors: true)}
    end
  end

  # TODO: transform to a modal from the current profile page
  def handle_event("validate", %{"user" => params}, socket) do
    current_user = socket.assigns.current_user

    form =
      AshPhoenix.Form.for_update(current_user, :change_password,
        domain: Accounts,
        as: "user"
      )
      |> AshPhoenix.Form.validate(params)

    {:noreply, assign(socket, :update_password_form, to_form(form))}
  end
end

FlyingNoodle

FlyingNoodle

can you show us your policies block in the user resource?

Guy14

Guy14 OP

It’s a very basic one for now:

policies do
    bypass AshAuthentication.Checks.AshAuthenticationInteraction do
      authorize_if always()
    end

    policy always() do
      authorize_if always()
    end
  end

For me the issue is not there, it’s in how to translate an AshAuthentication error to be able to output it in the form (to give the user a feedback).

To clarify: it’s normal that my test raises an AshAuthentication error. Regarding my :change_password action, I check the User’s current_password is valid before acting the password change, even if the user is already connected (it’s just an over-validation to ensure nobody else than the user did that password change in case someone just left his computer open without locking it).

So the error is normal in my test, but I would just like it to be translated into the form.

Guy14

Guy14 OP

I ended by implementing the AshPhoenix.FormData.Error for AshAuthentication.Errors.AuthenticationFailed. I’m not sure it is a good practice, but for now it does the job.

defimpl AshPhoenix.FormData.Error,
  for: AshAuthentication.Errors.AuthenticationFailed do
  def to_form_error(error) do
    {error.field, "authentication failed", []}
  end
end
— All posts loaded —

Where Next? Top

Trending in Questions Top

water
I’m aware of 2 such projects, are there others? git: GitHub - team-alembic/realworld: A fullstack Phoenix LiveView application with back...
New
katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
peck
Hi everyone. My team and I have been working on a fairly modest app based around video streaming and chat, but we’ve landed a customer t...
New
bradley
I really like the adapter patterns that ecto, nebulex, waffle, etc. use and would love find something similar for a key management servic...
New
unaware8150
Hello folks! So at work, we are seeing some situations where we have to define some “fixed” strings that are used across the codebase in...
New
Alvinkariuki
How Can I Optimise Compile Time Dependencies I have been building an elixir application for about 2 years now. Many modules and files ha...
New
leoschweizer
Hi, we run different LLM models across a cluster, with each node loading a configurable subset. Model-specific queues let nodes consume ...
New

Other Trending Topics Top

KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New
budgie
I love Elixir. It’s one of 2 programming languages I’ve ever fallen in love with. But I don’t use it anymore. Serverless was the promis...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews