Confirm with password

I want to perform deletion upon password confirmation. Any suggestions on how i can do that?
62 Replies
ZachDaniel
ZachDanielβ€’3y ago
What do you want to delete? Like you want to support the user deleting their account after verifying their password?
talha-azeem
talha-azeemOPβ€’3y ago
like i want to delete a member from team_joined_user table upon password confirmation I am thinking of fetching the password through a form and then verify it using current_user.hash_password and the password entered by user. for verification, I am thinking to use Bcrypt.verify_pass(password, current_user.hash_password) Purpose of asking here was that i might be able to customise the action of destroy for team_joined_user to verify the password and delete it or like to create a code_interface for the password verification πŸ˜…
ZachDaniel
ZachDanielβ€’3y ago
Well, I'd probably suggest that you could add a change in the action that checks the password of the current user before completing the action.
defmodule RequirePasswordConfirmation do
use Ash.Resource.Change

def change(changeset, opts, %{actor: actor}) do
Ash.Changeset.before_action(changeset, fn changeset ->
current_password = Ash.Changeset.get_argument(changeset, opts[:password])
if valid_password?(actor.hashed_password, current_password) do
changeset # don't mess with it if its valid
else
Ash.Changeset.add_error(changeset, field: opts[:password], message: "is incorrect")
end
end)
end
end

destroy :destroy do
argument :current_password, :string do
allow_nil? false
end

change {RequirePasswordConfirmation, password: :current_password}
end
defmodule RequirePasswordConfirmation do
use Ash.Resource.Change

def change(changeset, opts, %{actor: actor}) do
Ash.Changeset.before_action(changeset, fn changeset ->
current_password = Ash.Changeset.get_argument(changeset, opts[:password])
if valid_password?(actor.hashed_password, current_password) do
changeset # don't mess with it if its valid
else
Ash.Changeset.add_error(changeset, field: opts[:password], message: "is incorrect")
end
end)
end
end

destroy :destroy do
argument :current_password, :string do
allow_nil? false
end

change {RequirePasswordConfirmation, password: :current_password}
end
Might want to make that a special action called :destroy_with_password, or make the password allow_nil? true and ignore it if there is no current user, that way you have some way of destroying them without setting a current user if you don't want to
talha-azeem
talha-azeemOPβ€’3y ago
Should i be defining this in my junction table resource?
ZachDaniel
ZachDanielβ€’3y ago
In the above example, yes But there are lots of different ways you could do it, that is just an example of one of them Anyway, then when creating the form in your UI, you'd say Form.for_destroy(team_joined_user, :destroy, actor: <current user>, api: YourApi)
talha-azeem
talha-azeemOPβ€’3y ago
I will be creating this as a seprate module and then call this in my juction table resource?
ZachDaniel
ZachDanielβ€’3y ago
Well, kind of a "change" is a way to plug some logic into an action you don't "call it" per-se
destroy :destroy do
argument :current_password, :string do
allow_nil? false
end

change {RequirePasswordConfirmation, password: :current_password} # <- you add it to actions like this
end
destroy :destroy do
argument :current_password, :string do
allow_nil? false
end

change {RequirePasswordConfirmation, password: :current_password} # <- you add it to actions like this
end
talha-azeem
talha-azeemOPβ€’3y ago
for suppose i call it destroy_with_password then? and i will define it in require_password_confirmation.ex. if yes then shouldn't it be in
actions do
destroy :destroy_with_password do
argument :current_password, :string do
allow_nil? false
end
end
actions do
destroy :destroy_with_password do
argument :current_password, :string do
allow_nil? false
end
end
ZachDaniel
ZachDanielβ€’3y ago
Yes, it should be I was showing both parts in one example but the action goes in your actions, and the change goes in a different file
talha-azeem
talha-azeemOPβ€’3y ago
um, let me just clarify. so, i will be calling this in my actions of team_joined_user resource:
destroy :destroy_with_password do
argument :current_password, :string do
allow_nil? false
end

change {RequirePasswordConfirmation, password: :current_password}
end
destroy :destroy_with_password do
argument :current_password, :string do
allow_nil? false
end

change {RequirePasswordConfirmation, password: :current_password}
end
and this part will be in new file maybe called require_password_confirmation.ex containing:
defmodule RequirePasswordConfirmation do
use Ash.Resource.Change

def change(changeset, opts, %{actor: actor}) do
Ash.Changeset.before_action(changeset, fn changeset ->
current_password = Ash.Changeset.get_argument(changeset, opts[:password])
if valid_password?(actor.hashed_password, current_password) do
changeset # don't mess with it if its valid
else
Ash.Changeset.add_error(changeset, field: opts[:password], message: "is incorrect")
end
end)
end
end
defmodule RequirePasswordConfirmation do
use Ash.Resource.Change

def change(changeset, opts, %{actor: actor}) do
Ash.Changeset.before_action(changeset, fn changeset ->
current_password = Ash.Changeset.get_argument(changeset, opts[:password])
if valid_password?(actor.hashed_password, current_password) do
changeset # don't mess with it if its valid
else
Ash.Changeset.add_error(changeset, field: opts[:password], message: "is incorrect")
end
end)
end
end
right?
ZachDaniel
ZachDanielβ€’3y ago
Yes
talha-azeem
talha-azeemOPβ€’3y ago
and then call this for my form part Form.for_destroy(team_joined_user, :destroy, actor: <current user>, api: YourApi)
ZachDaniel
ZachDanielβ€’3y ago
Yep!
talha-azeem
talha-azeemOPβ€’3y ago
is this a built in function or do i need to define it ? valid_password?(actor.hashed_password, current_password)
ZachDaniel
ZachDanielβ€’3y ago
Depending on what your UI looks like you may need to change it, but that is a basic version that would allow you to select a join record, and make a join form for it that requires a correct password for the signed in user You need to define it
talha-azeem
talha-azeemOPβ€’3y ago
I am getting a hang of how things work in ASH eco system. I am very keen to explore more of it.
TeamJoinedUser
|> AshPhoenix.Form.for_destroy(:destroy_with_password,
api: Accounts,
actor: socket.assigns.current_user,
forms: [auto?: true]
)
TeamJoinedUser
|> AshPhoenix.Form.for_destroy(:destroy_with_password,
api: Accounts,
actor: socket.assigns.current_user,
forms: [auto?: true]
)
is this right?
ZachDaniel
ZachDanielβ€’3y ago
looks right to me πŸ‘
talha-azeem
talha-azeemOPβ€’3y ago
gives me an error πŸ˜…
ZachDaniel
ZachDanielβ€’3y ago
need a bit more information than that lol
talha-azeem
talha-azeemOPβ€’3y ago
sending
* (FunctionClauseError) no function clause matching in AshPhoenix.Form.for_destroy/3
* (FunctionClauseError) no function clause matching in AshPhoenix.Form.for_destroy/3
ZachDaniel
ZachDanielβ€’3y ago
oh, sorry
talha-azeem
talha-azeemOPβ€’3y ago
I can send you the stack trace in private if you want
ZachDaniel
ZachDanielβ€’3y ago
you need to provide the actual record as the first argument not TeamJoinedUser but the_team_joined_user_you_want_to_destroy
talha-azeem
talha-azeemOPβ€’3y ago
let me check
** (FunctionClauseError) no function clause matching in AppName.Accounts.RequirePasswordConfirmation.change/3
** (FunctionClauseError) no function clause matching in AppName.Accounts.RequirePasswordConfirmation.change/3
now it says it needs current_password as argument but i just want the form to be created for now
ZachDaniel
ZachDanielβ€’3y ago
Try removing the %{actor: actor} and see what is in the third argument, context
def change(changeset, opts, context) do
IO.inspect(context)
...
def change(changeset, opts, context) do
IO.inspect(context)
...
talha-azeem
talha-azeemOPβ€’3y ago
on it I found the mistake i thought i can replace the actor key with current_user but it was expecting actor
ZachDaniel
ZachDanielβ€’3y ago
yeah, its always called actor inside ash resources
talha-azeem
talha-azeemOPβ€’3y ago
so i will be doing the same i usually do for saving the form, right?
case AshPhoenix.Form.submit(socket.assigns.form) do
{:ok, _result} ->
{:noreply,
socket
|> put_flash(:info, "Member removed successfully!")
|> push_navigate(to: socket.assigns.navigate)}

{:error, form} ->
{:noreply, assign(socket, :form, form)}
end
case AshPhoenix.Form.submit(socket.assigns.form) do
{:ok, _result} ->
{:noreply,
socket
|> put_flash(:info, "Member removed successfully!")
|> push_navigate(to: socket.assigns.navigate)}

{:error, form} ->
{:noreply, assign(socket, :form, form)}
end
in save handle event
ZachDaniel
ZachDanielβ€’3y ago
Yep
talha-azeem
talha-azeemOPβ€’3y ago
something is wrong as i entered the wrong password and it still deleted the member πŸ˜…
ZachDaniel
ZachDanielβ€’3y ago
you'll have to debug the change and see if its getting there and whats going on
talha-azeem
talha-azeemOPβ€’3y ago
noted my valid_pass function is giving false but it still destroyed that entry
ZachDaniel
ZachDanielβ€’3y ago
And you’re adding an error to the changeset if it’s not valid?
talha-azeem
talha-azeemOPβ€’3y ago
yes the way you said
ZachDaniel
ZachDanielβ€’3y ago
Can i see the whole change as you have it now? The module I mean
talha-azeem
talha-azeemOPβ€’3y ago
defmodule AppName.Accounts.RequirePasswordConfirmation do
use Ash.Resource.Change

def change(changeset, opts, %{actor: actor}) do
Ash.Changeset.before_action(changeset, fn changeset ->
current_password = Ash.Changeset.get_argument(changeset, opts[:password])
if valid_password?(current_password, actor.hashed_password) do
changeset # don't mess with it if its valid
else
Ash.Changeset.add_error(changeset, field: opts[:password], message: "is incorrect")
end
end)
end

def valid_password?(password, hash_password) do
Bcrypt.verify_pass(password, hash_password)
end
end
defmodule AppName.Accounts.RequirePasswordConfirmation do
use Ash.Resource.Change

def change(changeset, opts, %{actor: actor}) do
Ash.Changeset.before_action(changeset, fn changeset ->
current_password = Ash.Changeset.get_argument(changeset, opts[:password])
if valid_password?(current_password, actor.hashed_password) do
changeset # don't mess with it if its valid
else
Ash.Changeset.add_error(changeset, field: opts[:password], message: "is incorrect")
end
end)
end

def valid_password?(password, hash_password) do
Bcrypt.verify_pass(password, hash_password)
end
end
ZachDaniel
ZachDanielβ€’3y ago
Can you inspect the changeset after adding the password? And make sure that there is an error there? Sorry After adding the error
talha-azeem
talha-azeemOPβ€’3y ago
yes i am on it
errors: [
%Ash.Error.Changes.InvalidAttribute{
field: :current_password,
message: "is incorrect",
private_vars: nil,
value: nil,
changeset: nil,
query: nil,
error_context: [],
vars: [field: :current_password, message: "is incorrect"],
path: [],
stacktrace: #Stacktrace<>,
class: :invalid
}
]
errors: [
%Ash.Error.Changes.InvalidAttribute{
field: :current_password,
message: "is incorrect",
private_vars: nil,
value: nil,
changeset: nil,
query: nil,
error_context: [],
vars: [field: :current_password, message: "is incorrect"],
path: [],
stacktrace: #Stacktrace<>,
class: :invalid
}
]
I have this in the inspect of
IO.inspect(Ash.Changeset.add_error(changeset, field: opts[:password], message: "is incorrect"), label: "here is the error changeset => ")
IO.inspect(Ash.Changeset.add_error(changeset, field: opts[:password], message: "is incorrect"), label: "here is the error changeset => ")
talha-azeem
talha-azeemOPβ€’3y ago
it said valid?: false and still deleted it.
No description
ZachDaniel
ZachDanielβ€’3y ago
this must be a bug with adding errors in before_action hooks have a fix, will push up after I run checks
talha-azeem
talha-azeemOPβ€’3y ago
you found the issue?
ZachDaniel
ZachDanielβ€’3y ago
yes
talha-azeem
talha-azeemOPβ€’3y ago
Please let me know once you push the changes so i can fetch the latest version
ZachDaniel
ZachDanielβ€’3y ago
Hey @talha-azeem can you please check the behavior when using with the main branch of Ash? {:ash, github: "ash-project/ash"}
talha-azeem
talha-azeemOPβ€’3y ago
sorry for the late reply. Let me try and update you still same issue
ZachDaniel
ZachDanielβ€’3y ago
πŸ€” were you already on a github release before? Or did you just switch
talha-azeem
talha-azeemOPβ€’3y ago
i just did let me try to put the whole link i deleted the deps and now fetching it again now it is pulling from the github let me update you in a min
** (UndefinedFunctionError) function Ash.NotLoaded.__changeset__/0 is undefined or private
** (UndefinedFunctionError) function Ash.NotLoaded.__changeset__/0 is undefined or private
I am getting this error if i fetch it directly from github while trying to add a record in my junction table which was working fine before
ZachDaniel
ZachDanielβ€’3y ago
yeah, please update ash_postgres to the latest in addition to this
talha-azeem
talha-azeemOPβ€’3y ago
same behavior valid_password/2 returns false. It goes to the else part adds the error and then deletes it πŸ˜…
talha-azeem
talha-azeemOPβ€’3y ago
No description
talha-azeem
talha-azeemOPβ€’3y ago
errors: [
%Ash.Error.Changes.InvalidAttribute{
field: :current_password,
message: "is incorrect",
private_vars: nil,
value: nil,
changeset: nil,
query: nil,
error_context: [],
vars: [field: :current_password, message: "is incorrect"],
path: [],
stacktrace: #Stacktrace<>,
class: :invalid
}
]
errors: [
%Ash.Error.Changes.InvalidAttribute{
field: :current_password,
message: "is incorrect",
private_vars: nil,
value: nil,
changeset: nil,
query: nil,
error_context: [],
vars: [field: :current_password, message: "is incorrect"],
path: [],
stacktrace: #Stacktrace<>,
class: :invalid
}
]
errors part from the changeset ☝🏻
ZachDaniel
ZachDanielβ€’3y ago
ah, yeah sorry missed one thing
talha-azeem
talha-azeemOPβ€’3y ago
No Problem. I appreciate the fact that you are providing me fix on the run so that my work can continue ❀️
ZachDaniel
ZachDanielβ€’3y ago
okay try main now please πŸ™‚ use mix deps.update ash to fetch the latest
talha-azeem
talha-azeemOPβ€’3y ago
on it kudos @Zach Daniel , It works now
ZachDaniel
ZachDanielβ€’3y ago
πŸ₯³
talha-azeem
talha-azeemOPβ€’3y ago
❀️
jart
jartβ€’3y ago
Nice work team.
talha-azeem
talha-azeemOPβ€’3y ago
Great Work 🫢🏻 @Zach Daniel , A quick question. Can i utilise your ash-hq user settings page?
ZachDaniel
ZachDanielβ€’3y ago
Its MIT license, you can do whatever you want with that code πŸ‘ you could copy it entirely if you want
talha-azeem
talha-azeemOPβ€’3y ago
okay can you tell one thing? πŸ˜…
ZachDaniel
ZachDanielβ€’3y ago
please make a new thread for it, but yes πŸ™‚

Did you find this page helpful?