ash_graphql update without authorizing the read action

I have a user ash resource (not using ash_authentication here) whose read action is gated by policies. I'm adding a reset_password action, which will take a reset token we emailed to the user and their new password, and if the token matches, will update their password (all of this via ash_graphql from an SPA). Because of the nature of this workflow, there will necessarily be no actor present when this action is called. In direct elixir code (such as in unit tests), this works great:
user
|> Ash.Changeset.for_update(:reset_password, %{ ... }, actor: nil)
|> Ash.update!()
user
|> Ash.Changeset.for_update(:reset_password, %{ ... }, actor: nil)
|> Ash.update!()
but via graphql mutation, this becomes an issue because ash_graphql uses the resource's read action, which is gated by policies., so the read phase of the graphql transaction fails because it is filtering based on the actor (which does not exist here). I'm wondering if it's possible to disable authorization on just the read action for a graphql call? Or is my only option in this case to create a separate read action w/ no policies and use read_action :unfiltered_read or something?
9 Replies
Jesse Williams
Jesse WilliamsOP•4w ago
I'm imagining something like authorize_read? on the update DSL options
Torkan
Torkan•4w ago
Hm, either that or creating a generic action, where you just do the same read with authorize?: false
Jesse Williams
Jesse WilliamsOP•4w ago
Yeah, I was considering that
Torkan
Torkan•4w ago
that = create a read action without any policies 😛
Jesse Williams
Jesse WilliamsOP•4w ago
a generic action feels kinda annoying, since it requires wiring everything up manually, but seems like it's probably the most explicit way of doing this in the current version of ash_graphql. Would a PR be accepted that added an authorize_read? opt on the update DSL options?
ZachDaniel
ZachDaniel•4w ago
Hmm...there is another way to do it You can set the read_action option to a read action that allows all reads But that's not ideal because you could accidentally expose the record Actually, you don't want to do what you're trying to do I think You do want a generic action You're exposing the ability to read the user record and any other fields just by knowing the email rights
Jesse Williams
Jesse WilliamsOP•4w ago
I had considered that. On its face it feels potentially problematic, but thinking further, they did just reset the user's password, so they'd be able to get access to that user record by just going and logging in with the password they just reset to, right? So in practice kinda feels like it'd be fine But I can see why a generic action is potentially a bit of a tighter representation of this. I'll probably go that route
ZachDaniel
ZachDaniel•4w ago
oh, it requires current password? Then yeah its fine in that case
Jesse Williams
Jesse WilliamsOP•4w ago
well, 2 use cases. One is a token we send via email to validate identity (doesn't require an actor because you could be logged out, like forgot password situation). One is provide current password + new password (does require an actor, like a PW reset whilw logged in). Regardless, I ended up going with the generic action route anyways. but in both use cases, you're providing something that confirms you are allowed to change the user's password, which would also imply access to the user record itself. Although, I could see an argument that depending on policies and/or field policies you might not be able to access "your own" user record and/or all fields of it, so exposing it fully unauthorized is dangerous. In our case the policies are pretty simply just that you can always read your own user record though so it's kinda fine

Did you find this page helpful?