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

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
jonnycharles
I’m in search of an Elixir library that offers PDF generation capabilities similar to Ruby’s Prawn. While there have been discussions abo...
New
spammy
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
dli
Before I dive in myself, did anyone successfully sprinkle Hologram into their existing LiveView app? Looking for hints regarding: Addi...
New
roeland
Kia ora, We have been using elixir-google-api to connect to Google Drive. However, with the updates to Tesla due to CVEs this is now bro...
New
bottlenecked
Hi all, I wanted to ask how the community is dealing with post-release steps. Today we have Ecto migrations, which make sure that the db...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
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
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
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