AshAuthentication failures with custom LiveViews

Hello, I want to have my own custom LiveViews for my auth routes, and I read https://alembic.com.au/blog/customising-ash-authentication-with-phoenix-liveview which was very helpful. I am aware of the overrides, but I don't really like them. Here are some relevant code snippets:
# index.ex
defp apply_action(socket, :sign_in, params) do
form = MyApp.Accounts.form_to_sign_in_with_password()
socket
|> assign(:action, ~p"/auth/user/password/sign_in")
|> assign(:form, to_form(form))
end

# form.ex
# render()
<.form
for={@form}
id="auth-form"
phx-submit="submit"
phx-trigger-action={@trigger_action}
phx-target={@myself}
action={@action}
method="POST"
>
<fieldset>

<.input
field={@form[:email]}
label="Email"
type="email"
/>
<.input
field={@form[:password]}
label="Password"
type="password"
/>
<.button type="submit">{@cta}</.button>
</fieldset>
...

@impl true
def handle_event("submit", %{"form" => params}, socket) do
form = AshPhoenix.Form.validate(socket.assigns.form, params)

socket =
socket
|> assign(:form, form)
|> assign(:trigger_action, form.source.valid?)

{:noreply, socket}
end
# index.ex
defp apply_action(socket, :sign_in, params) do
form = MyApp.Accounts.form_to_sign_in_with_password()
socket
|> assign(:action, ~p"/auth/user/password/sign_in")
|> assign(:form, to_form(form))
end

# form.ex
# render()
<.form
for={@form}
id="auth-form"
phx-submit="submit"
phx-trigger-action={@trigger_action}
phx-target={@myself}
action={@action}
method="POST"
>
<fieldset>

<.input
field={@form[:email]}
label="Email"
type="email"
/>
<.input
field={@form[:password]}
label="Password"
type="password"
/>
<.button type="submit">{@cta}</.button>
</fieldset>
...

@impl true
def handle_event("submit", %{"form" => params}, socket) do
form = AshPhoenix.Form.validate(socket.assigns.form, params)

socket =
socket
|> assign(:form, form)
|> assign(:trigger_action, form.source.valid?)

{:noreply, socket}
end
For some reason, when the POST request is triggered the auth fails as if no args were passed:
[info] POST /auth/user/password/sign_in
[debug] Processing with AshAuthentication.Phoenix.StrategyRouter
Parameters: %{"_csrf_token" => "SnhLCUgJNEcYFBhaKF8rHw1IDTtcAihB9-rXqOq0zxWcdfCLlzHK4vF2", "form" => %{"email" => "a@a.a", "password" => "[FILTERED]"}}
Pipelines: [:browser]
[warning] Authentication failed:
Invalid Error

* argument password is required
...
* argument email is required
...
[info] POST /auth/user/password/sign_in
[debug] Processing with AshAuthentication.Phoenix.StrategyRouter
Parameters: %{"_csrf_token" => "SnhLCUgJNEcYFBhaKF8rHw1IDTtcAihB9-rXqOq0zxWcdfCLlzHK4vF2", "form" => %{"email" => "a@a.a", "password" => "[FILTERED]"}}
Pipelines: [:browser]
[warning] Authentication failed:
Invalid Error

* argument password is required
...
* argument email is required
...
In short, when I POST at /auth/user/password/sign_in the form args supposedly are missing, but as you can see from above they don't and the form is valid. Any help is appreciated!
Alembic
Customising Ash Authentication with Phoenix LiveView
Learn how to implement and customise Ash Authentication in Elixir applications using simple DSL on Ash resources with Phoenix LiveView integration.
Solution:
I just figured it out 🙂 If someone could explain why the following worked, that would be awesome! The issue was that originally I couldn't sign in nor register....
Jump to solution
2 Replies
barnabasj
barnabasj•2mo ago
🤔 In another thing I have with magic link the token isn't nested under the "form" key, not sure where the difference is though
Solution
theopechli
theopechli•2mo ago
I just figured it out 🙂 If someone could explain why the following worked, that would be awesome! The issue was that originally I couldn't sign in nor register.
# index.ex
# In my actions I replaced this
# |> assign(:form, to_form(MyApp.Accounts.form_to_register_with_password()))
# with this
|> assign(
:form,
AshPhoenix.Form.for_create(MyApp.Accounts.User, :register_with_password, api: MyApp.Accounts, as: "user")
)

# form.ex
<.form
# I added this in the form
:let={f}
# ...
# in my inputs
<.input
field={f[:email]}
# ...

# In the events
def handle_event("validate", %{"user" => params}, socket) do
# index.ex
# In my actions I replaced this
# |> assign(:form, to_form(MyApp.Accounts.form_to_register_with_password()))
# with this
|> assign(
:form,
AshPhoenix.Form.for_create(MyApp.Accounts.User, :register_with_password, api: MyApp.Accounts, as: "user")
)

# form.ex
<.form
# I added this in the form
:let={f}
# ...
# in my inputs
<.input
field={f[:email]}
# ...

# In the events
def handle_event("validate", %{"user" => params}, socket) do
In my Accounts domain I do not have any forms, and in the defines I have:
define :register_with_password, args: [:email, :password, :password_confirmation]
define :sign_in_with_password, args: [:email, :password]
define :register_with_password, args: [:email, :password, :password_confirmation]
define :sign_in_with_password, args: [:email, :password]
Of course, that was written in the aforementioned blog post, but I thought that it'd be better to use the form_to_ variants to create the forms... Anywho, if someone could explain why this worked I'd appreciate it!

Did you find this page helpful?