AF
Ash Framework•4mo ago
Geril

Auto generated `reset_password_with_token` not loading user

Hi everyone! 👋 I’m trying to expose the auto-generated reset_password_with_token action via Ash GraphQL, but I keep getting a not_found error no matter what I try. Here’s my setup (auto generated):
update :reset_password_with_token do
argument :reset_token, :string do
allow_nil? false
sensitive? true
end

argument :password, :string do
description "The proposed password for the user, in plain text."
allow_nil? false
constraints min_length: 8
sensitive? true
end

argument :password_confirmation, :string do
description "The proposed password for the user (again), in plain text."
allow_nil? false
sensitive? true
end

# validates the provided reset token
validate AshAuthentication.Strategy.Password.ResetTokenValidation

# validates that the password matches the confirmation
validate AshAuthentication.Strategy.Password.PasswordConfirmationValidation

# Hashes the provided password
change AshAuthentication.Strategy.Password.HashPasswordChange

# Generates an authentication token for the user
change AshAuthentication.GenerateTokenChange
end
update :reset_password_with_token do
argument :reset_token, :string do
allow_nil? false
sensitive? true
end

argument :password, :string do
description "The proposed password for the user, in plain text."
allow_nil? false
constraints min_length: 8
sensitive? true
end

argument :password_confirmation, :string do
description "The proposed password for the user (again), in plain text."
allow_nil? false
sensitive? true
end

# validates the provided reset token
validate AshAuthentication.Strategy.Password.ResetTokenValidation

# validates that the password matches the confirmation
validate AshAuthentication.Strategy.Password.PasswordConfirmationValidation

# Hashes the provided password
change AshAuthentication.Strategy.Password.HashPasswordChange

# Generates an authentication token for the user
change AshAuthentication.GenerateTokenChange
end
GraphQL block:
mutations do
update :reset_password_with_token, :reset_password_with_token do
identity :unique_email
end

action :request_password_reset_token, :request_password_reset_token
mutations do
update :reset_password_with_token, :reset_password_with_token do
identity :unique_email
end

action :request_password_reset_token, :request_password_reset_token
I am calling the API like so:
mutation RequestPasswordResetToken {
requestPasswordResetToken(input: { email: "email@dom.com" })
}

mutation ResetPasswordWithToken {
resetPasswordWithToken(
input: {
password: "123456789"
passwordConfirmation: "123456789"
resetToken: "token_from_previous_step"
}
email: "email@dom.com"
) {
result {
id
email
}
}
}
mutation RequestPasswordResetToken {
requestPasswordResetToken(input: { email: "email@dom.com" })
}

mutation ResetPasswordWithToken {
resetPasswordWithToken(
input: {
password: "123456789"
passwordConfirmation: "123456789"
resetToken: "token_from_previous_step"
}
email: "email@dom.com"
) {
result {
id
email
}
}
}
I also tried without the identity block and passing the token in headers - no luck. Shouldn’t the token automatically load the user? Or am I missing a step? Would really appreciate help from anyone who’s done this before!
Solution:
seems like: ``` bypass action(:read) do authorize_if expr(id == ^actor(:id)) end...
Jump to solution
9 Replies
Rebecca Le
Rebecca Le•4mo ago
"not found" is kind of vague - what's the exact error you're seeing or what are your logs reporting?
Geril
GerilOP•4mo ago
{
"data": {
"resetPasswordWithToken": null
},
"errors": [
{
"code": "not_found",
"message": "could not be found",
"path": [
"resetPasswordWithToken"
],
"fields": [
"email"
],
"vars": [],
"locations": [
{
"line": 2,
"column": 3
}
],
"short_message": "could not be found"
}
]
}
{
"data": {
"resetPasswordWithToken": null
},
"errors": [
{
"code": "not_found",
"message": "could not be found",
"path": [
"resetPasswordWithToken"
],
"fields": [
"email"
],
"vars": [],
"locations": [
{
"line": 2,
"column": 3
}
],
"short_message": "could not be found"
}
]
}
Rebecca Le
Rebecca Le•4mo ago
right, what do your logs say? is it failing at trying to look up a user, or doing something else?
Geril
GerilOP•4mo ago
no errors reported in console, only log about mutation call:
[info] POST /graphql
[debug] Processing with Absinthe.Plug
Parameters: %{"operationName" => "ResetPasswordWithToken", "query" => "mutation ResetPasswordWithToken {\n resetPasswordWithToken(\n input: {\n password: \"123456789\"\n passwordConfirmation: \"123456789\"\n resetToken: \"token\"\n }\n email: \"email@dom.com\"\n ) {\n result {\n id\n email\n }\n }\n}\n", "variables" => %{}}
Pipelines: [:graphql]
[debug] ABSINTHE schema=HatatitlaWeb.GraphqlSchema variables=%{}
---
mutation ResetPasswordWithToken {
resetPasswordWithToken(
input: {
password: "123456789"
passwordConfirmation: "123456789"
resetToken: "token"
}
email: "email@dom.com"
) {
result {
id
email
}
}
}
---
[info] Sent 200 in 504ms
[info] POST /graphql
[debug] Processing with Absinthe.Plug
Parameters: %{"operationName" => "ResetPasswordWithToken", "query" => "mutation ResetPasswordWithToken {\n resetPasswordWithToken(\n input: {\n password: \"123456789\"\n passwordConfirmation: \"123456789\"\n resetToken: \"token\"\n }\n email: \"email@dom.com\"\n ) {\n result {\n id\n email\n }\n }\n}\n", "variables" => %{}}
Pipelines: [:graphql]
[debug] ABSINTHE schema=HatatitlaWeb.GraphqlSchema variables=%{}
---
mutation ResetPasswordWithToken {
resetPasswordWithToken(
input: {
password: "123456789"
passwordConfirmation: "123456789"
resetToken: "token"
}
email: "email@dom.com"
) {
result {
id
email
}
}
}
---
[info] Sent 200 in 504ms
when I try with identity false and call it, I get back similar error only with email no longer being present in fields
{
"data": {
"resetPasswordWithToken": null
},
"errors": [
{
"code": "not_found",
"message": "could not be found",
"path": [
"resetPasswordWithToken"
],
"fields": [],
"vars": [],
"locations": [
{
"line": 2,
"column": 3
}
],
"short_message": "could not be found"
}
]
}
{
"data": {
"resetPasswordWithToken": null
},
"errors": [
{
"code": "not_found",
"message": "could not be found",
"path": [
"resetPasswordWithToken"
],
"fields": [],
"vars": [],
"locations": [
{
"line": 2,
"column": 3
}
],
"short_message": "could not be found"
}
]
}
here is my whole auth config:
authentication do
add_ons do
log_out_everywhere do
apply_on_password_change? true
end

confirmation :confirm_new_user do
monitor_fields [:email]
confirm_on_create? true
confirm_on_update? false
require_interaction? false
confirmed_at_field :confirmed_at
auto_confirm_actions [:reset_password_with_token]
sender Hatatitla.Accounts.User.Senders.SendNewUserConfirmationEmail
end
end

tokens do
enabled? true
token_resource Hatatitla.Accounts.Token
signing_secret Hatatitla.Secrets
store_all_tokens? true
require_token_presence_for_authentication? true
token_lifetime {300, :days}
end

strategies do
password :password do
identity_field :email
hash_provider AshAuthentication.BcryptProvider

resettable do
sender Hatatitla.Accounts.User.Senders.SendPasswordResetEmail
token_lifetime {300, :days}
password_reset_action_name :reset_password_with_token
request_password_reset_action_name :request_password_reset_token
end
end
end
end
authentication do
add_ons do
log_out_everywhere do
apply_on_password_change? true
end

confirmation :confirm_new_user do
monitor_fields [:email]
confirm_on_create? true
confirm_on_update? false
require_interaction? false
confirmed_at_field :confirmed_at
auto_confirm_actions [:reset_password_with_token]
sender Hatatitla.Accounts.User.Senders.SendNewUserConfirmationEmail
end
end

tokens do
enabled? true
token_resource Hatatitla.Accounts.Token
signing_secret Hatatitla.Secrets
store_all_tokens? true
require_token_presence_for_authentication? true
token_lifetime {300, :days}
end

strategies do
password :password do
identity_field :email
hash_provider AshAuthentication.BcryptProvider

resettable do
sender Hatatitla.Accounts.User.Senders.SendPasswordResetEmail
token_lifetime {300, :days}
password_reset_action_name :reset_password_with_token
request_password_reset_action_name :request_password_reset_token
end
end
end
end
I also tried corrupting the token & I am getting:
{
"data": {
"resetPasswordWithToken": null
},
"errors": [
{
"code": "invalid_argument",
"message": "is not valid",
"path": [
"resetPasswordWithToken"
],
"fields": [
"reset_token"
],
"vars": {},
"locations": [
{
"line": 2,
"column": 3
}
],
"short_message": "is not valid"
}
]
}
{
"data": {
"resetPasswordWithToken": null
},
"errors": [
{
"code": "invalid_argument",
"message": "is not valid",
"path": [
"resetPasswordWithToken"
],
"fields": [
"reset_token"
],
"vars": {},
"locations": [
{
"line": 2,
"column": 3
}
],
"short_message": "is not valid"
}
]
}
that makes me believe the token is right I also try logging out at ResetTokenValidation to check if it goes over that and it did.
ZachDaniel
ZachDaniel•4mo ago
What do your policies look like?
Geril
GerilOP•4mo ago
policies do
bypass AshAuthentication.Checks.AshAuthenticationInteraction do
authorize_if always()
end

bypass action(:register_with_password) do
authorize_if always()
end

bypass action(:sign_in_with_password) do
authorize_if always()
end

bypass action(:request_password_reset_token) do
authorize_if always()
end

bypass action(:reset_password_with_token) do
authorize_if always()
end

bypass action(:read) do
authorize_if expr(id == ^actor(:id))
end

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

bypass action(:register_with_password) do
authorize_if always()
end

bypass action(:sign_in_with_password) do
authorize_if always()
end

bypass action(:request_password_reset_token) do
authorize_if always()
end

bypass action(:reset_password_with_token) do
authorize_if always()
end

bypass action(:read) do
authorize_if expr(id == ^actor(:id))
end

policy always() do
forbid_if always()
end
end
Solution
Geril
Geril•4mo ago
seems like:
bypass action(:read) do
authorize_if expr(id == ^actor(:id))
end
bypass action(:read) do
authorize_if expr(id == ^actor(:id))
end
is the issue 🤦‍♂️ after switching it to authorize_if always() it works now. I wonder how can I prevent users from reading the info of other users? 🤔
ZachDaniel
ZachDaniel•4mo ago
Good question 🙂 Your best bet in the short term is to wrap the get/update in a generic action 🙂
Geril
GerilOP•4mo ago
I see, so basically within that newly formed action will be the "lookup" and user will be passed down to reset action, right?

Did you find this page helpful?