Issues using AshAuthentication's sign_in_with_password action with AshGraphql

defmodule Coruscant.Accounts.User do
use Ash.Resource,
data_layer: AshPostgres.DataLayer,
extensions: [AshAuthentication, AshGraphql.Resource]

code_interface do
define_for Coruscant.Accounts
define :register_with_password, args: [:name, :password, :password_confirmation]
define :sign_in_with_password, args: [:name, :password]
end

graphql do
type :user

queries do
list :list_users, :read
end

mutations do
create :register, :register_with_password
# if this is uncommented it causes a compile error
# create :sign_in, :sign_in_with_password
end
end

actions do
defaults [:read]
end

attributes do
uuid_primary_key :id

attribute :name, :string do
allow_nil? false
end

attribute :hashed_password, :string do
allow_nil? false
sensitive? true
private? true
end
end

authentication do
api Coruscant.Accounts

strategies do
password :password do
identity_field :name
end
end

tokens do
enabled? true
token_resource Coruscant.Accounts.Token

signing_secret fn _, _ ->
Application.fetch_env(:coruscant, :token_signing_secret)
end
end
end

postgres do
table "users"
repo Coruscant.Repo
end

identities do
identity :unique_name, [:name]
end
end
defmodule Coruscant.Accounts.User do
use Ash.Resource,
data_layer: AshPostgres.DataLayer,
extensions: [AshAuthentication, AshGraphql.Resource]

code_interface do
define_for Coruscant.Accounts
define :register_with_password, args: [:name, :password, :password_confirmation]
define :sign_in_with_password, args: [:name, :password]
end

graphql do
type :user

queries do
list :list_users, :read
end

mutations do
create :register, :register_with_password
# if this is uncommented it causes a compile error
# create :sign_in, :sign_in_with_password
end
end

actions do
defaults [:read]
end

attributes do
uuid_primary_key :id

attribute :name, :string do
allow_nil? false
end

attribute :hashed_password, :string do
allow_nil? false
sensitive? true
private? true
end
end

authentication do
api Coruscant.Accounts

strategies do
password :password do
identity_field :name
end
end

tokens do
enabled? true
token_resource Coruscant.Accounts.Token

signing_secret fn _, _ ->
Application.fetch_env(:coruscant, :token_signing_secret)
end
end
end

postgres do
table "users"
repo Coruscant.Repo
end

identities do
identity :unique_name, [:name]
end
end
66 Replies
ZachDaniel
ZachDaniel3y ago
What is the error?
csos95
csos95OP3y ago
Compiling 3 files (.ex)

== Compilation error in file lib/coruscant/schema.ex ==
** (KeyError) key :accept not found in: %Ash.Resource.Actions.Read{name: :sign_in_with_password, pagination: false, primary?: false, filter: nil, description: nil, get?: true, manual: nil, modify_query: nil, transaction?: false, arguments: [%Ash.Resource.Actions.Argument{allow_nil?: false, type: Ash.Type.String, name: :name, default: nil, private?: false, sensitive?: false, description: nil, constraints: [allow_empty?: false, trim?: true]}, %Ash.Resource.Actions.Argument{allow_nil?: false, type: Ash.Type.String, name: :password, default: nil, private?: false, sensitive?: true, description: nil, constraints: [allow_empty?: false, trim?: true]}], preparations: [%Ash.Resource.Preparation{preparation: {AshAuthentication.Strategy.Password.SignInPreparation, []}}], touches_resources: [], type: :read}
(ash_graphql 0.22.4) lib/resource/resource.ex:760: anonymous fn/2 in AshGraphql.Resource.mutation_fields/4
(elixir 1.14.3) lib/enum.ex:4197: Enum.filter_list/2
(ash_graphql 0.22.4) lib/resource/resource.ex:759: AshGraphql.Resource.mutation_fields/4
(ash_graphql 0.22.4) lib/resource/resource.ex:438: anonymous fn/4 in AshGraphql.Resource.mutations/3
(elixir 1.14.3) lib/enum.ex:1658: Enum."-map/2-lists^map/1-0-"/2
(elixir 1.14.3) lib/enum.ex:1658: Enum."-map/2-lists^map/1-0-"/2
(ash_graphql 0.22.4) lib/resource/resource.ex:410: AshGraphql.Resource.mutations/3
(elixir 1.14.3) lib/enum.ex:4249: Enum.flat_map_list/2
(coruscant 0.1.0) lib/coruscant/schema.ex:6: Coruscant.Accounts.AshTypes.run/2
(absinthe 1.7.0) lib/absinthe/pipeline.ex:384: Absinthe.Pipeline.run_phase/3
(absinthe 1.7.0) lib/absinthe/schema.ex:360: Absinthe.Schema.__after_compile__/2
(stdlib 4.2) lists.erl:1350: :lists.foldl/3
(elixir 1.14.3) lib/kernel/parallel_compiler.ex:340: anonymous fn/5 in Kernel.ParallelCompiler.spawn_workers/7
Generated coruscant app
Compiling 3 files (.ex)

== Compilation error in file lib/coruscant/schema.ex ==
** (KeyError) key :accept not found in: %Ash.Resource.Actions.Read{name: :sign_in_with_password, pagination: false, primary?: false, filter: nil, description: nil, get?: true, manual: nil, modify_query: nil, transaction?: false, arguments: [%Ash.Resource.Actions.Argument{allow_nil?: false, type: Ash.Type.String, name: :name, default: nil, private?: false, sensitive?: false, description: nil, constraints: [allow_empty?: false, trim?: true]}, %Ash.Resource.Actions.Argument{allow_nil?: false, type: Ash.Type.String, name: :password, default: nil, private?: false, sensitive?: true, description: nil, constraints: [allow_empty?: false, trim?: true]}], preparations: [%Ash.Resource.Preparation{preparation: {AshAuthentication.Strategy.Password.SignInPreparation, []}}], touches_resources: [], type: :read}
(ash_graphql 0.22.4) lib/resource/resource.ex:760: anonymous fn/2 in AshGraphql.Resource.mutation_fields/4
(elixir 1.14.3) lib/enum.ex:4197: Enum.filter_list/2
(ash_graphql 0.22.4) lib/resource/resource.ex:759: AshGraphql.Resource.mutation_fields/4
(ash_graphql 0.22.4) lib/resource/resource.ex:438: anonymous fn/4 in AshGraphql.Resource.mutations/3
(elixir 1.14.3) lib/enum.ex:1658: Enum."-map/2-lists^map/1-0-"/2
(elixir 1.14.3) lib/enum.ex:1658: Enum."-map/2-lists^map/1-0-"/2
(ash_graphql 0.22.4) lib/resource/resource.ex:410: AshGraphql.Resource.mutations/3
(elixir 1.14.3) lib/enum.ex:4249: Enum.flat_map_list/2
(coruscant 0.1.0) lib/coruscant/schema.ex:6: Coruscant.Accounts.AshTypes.run/2
(absinthe 1.7.0) lib/absinthe/pipeline.ex:384: Absinthe.Pipeline.run_phase/3
(absinthe 1.7.0) lib/absinthe/schema.ex:360: Absinthe.Schema.__after_compile__/2
(stdlib 4.2) lists.erl:1350: :lists.foldl/3
(elixir 1.14.3) lib/kernel/parallel_compiler.ex:340: anonymous fn/5 in Kernel.ParallelCompiler.spawn_workers/7
Generated coruscant app
ZachDaniel
ZachDaniel3y ago
Ah, yeah So the sign in action is a read action.
csos95
csos95OP3y ago
So do I put that under queries with read_one?
ZachDaniel
ZachDaniel3y ago
Yep, and if you want it to be a mutation in the actual graphql you can pass as_mutation? true in that declaration.
csos95
csos95OP3y ago
It looks like read_one doesn't have that flag, but get does.
ZachDaniel
ZachDaniel3y ago
I think you may want to do it with get Ha, yeah Was just double checking
csos95
csos95OP3y ago
Does that not require the id?
ZachDaniel
ZachDaniel3y ago
Ash HQ
DSL Entity: ash_graphql > AshGraphql Resource > graphql > queries >...
View the documentation for DSL Entity: ash_graphql > AshGraphql Resource > graphql > queries > get on Ash HQ.
ZachDaniel
ZachDaniel3y ago
Somethings off with the docs descriptions on that page… Either way, pass identity nil And it won’t require an id
csos95
csos95OP3y ago
Yeah, I noticed the values for the doc column on those pages disappeared recently.
ZachDaniel
ZachDaniel3y ago
Ill fix it tonight 👍 Hexdocs is a good backup https://hexdocs.pm/ash_graphql/AshGraphql.Resource.html#module-queries
csos95
csos95OP3y ago
Oh wow, that's a lot more info! I hadn't checked hexdocs for ash at all.
ZachDaniel
ZachDaniel3y ago
Yeah…we should probably have a view like that in ash hq instead of hiding it in the sidebar fixed the missing DSL docs. @csos95 Did you get things figured out? If so, please mark this post with the solved tag and close it by right clicking on the thread in the left bar and closing it 🙇
csos95
csos95OP3y ago
Sorry, I had to make dinner and then forgot to follow up. It shows up in my graphql schema now as a mutation with the correct parameters, but if I try to call it I get a forbidden error, but it says there are no policy errors.
{
"data": {
"signIn": null
},
"errors": [
{
"code": "Forbidden",
"fields": [],
"locations": [
{
"column": 3,
"line": 2
}
],
"message": "No policy errors",
"path": [
"signIn"
],
"short_message": "forbidden",
"vars": {}
}
]
}
{
"data": {
"signIn": null
},
"errors": [
{
"code": "Forbidden",
"fields": [],
"locations": [
{
"column": 3,
"line": 2
}
],
"message": "No policy errors",
"path": [
"signIn"
],
"short_message": "forbidden",
"vars": {}
}
]
}
I added a modify_resolution that just prints the state and returns it to see if I can find any errors. I haven't seen any yet, but there's a lot of output so I might be missing something.
ZachDaniel
ZachDaniel3y ago
Do you have policies on your resource?
csos95
csos95OP3y ago
policies do
bypass always() do
authorize_if AshAuthentication.Checks.AshAuthenticationInteraction
end

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

policy always() do
authorize_if always()
end
end
I had the second one to let everything through, but I added the first from the ash authentication guide just to be safe so I don't forget it later.
ZachDaniel
ZachDaniel3y ago
Hm…well. That ought to let anything happen. Im not sure about the “no policy errors” thing. I don’t recognize that error message
csos95
csos95OP3y ago
GitHub
ash/policy.ex at 2f3fcbad13cc23c6ac67af4ad13c31b7751003b2 · ash-pro...
A declarative and extensible framework for building Elixir applications. - ash/policy.ex at 2f3fcbad13cc23c6ac67af4ad13c31b7751003b2 · ash-project/ash
ZachDaniel
ZachDaniel3y ago
Oh okay that’s the breakdown Can you try calling the action in code? Then you can see the error itself. I.e ash.Query.for_read(:sign_in, …)
csos95
csos95OP3y ago
Calling it in code seems to work fine.
iex(4)> Ash.Query.for_read(Coruscant.Accounts.User, :sign_in_with_password, %{name: "foo", password: "password"}) |> Coruscant.Accounts.read()
[debug] QUERY OK source="users" db=0.6ms decode=1.6ms queue=0.7ms idle=1945.9ms
SELECT u0."id", u0."name", u0."hashed_password" FROM "users" AS u0 WHERE (u0."name"::text = $1::text) ["foo"]
AshPostgres.DataLayer.run_query/2, at: lib/data_layer.ex:599
{:ok,
[
#Coruscant.Accounts.User<
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
id: "7218de28-9c72-4fa8-82f2-45ea2b06feb9",
name: "foo",
aggregates: %{},
calculations: %{},
__order__: nil,
...
>
]}
iex(4)> Ash.Query.for_read(Coruscant.Accounts.User, :sign_in_with_password, %{name: "foo", password: "password"}) |> Coruscant.Accounts.read()
[debug] QUERY OK source="users" db=0.6ms decode=1.6ms queue=0.7ms idle=1945.9ms
SELECT u0."id", u0."name", u0."hashed_password" FROM "users" AS u0 WHERE (u0."name"::text = $1::text) ["foo"]
AshPostgres.DataLayer.run_query/2, at: lib/data_layer.ex:599
{:ok,
[
#Coruscant.Accounts.User<
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
id: "7218de28-9c72-4fa8-82f2-45ea2b06feb9",
name: "foo",
aggregates: %{},
calculations: %{},
__order__: nil,
...
>
]}
ZachDaniel
ZachDaniel3y ago
Do you have tokens enabled? You might need to add your catch all policy to your tokens resource too
csos95
csos95OP3y ago
Here's the authentication section in my user resource.
authentication do
api Coruscant.Accounts

strategies do
password :password do
identity_field :name
end
end

tokens do
enabled? true
token_resource Coruscant.Accounts.Token

signing_secret fn _, _ ->
Application.fetch_env(:coruscant, :token_signing_secret)
end
end
end
authentication do
api Coruscant.Accounts

strategies do
password :password do
identity_field :name
end
end

tokens do
enabled? true
token_resource Coruscant.Accounts.Token

signing_secret fn _, _ ->
Application.fetch_env(:coruscant, :token_signing_secret)
end
end
end
And I just added the policy to my token.
defmodule Coruscant.Accounts.Token do
use Ash.Resource,
data_layer: AshPostgres.DataLayer,
extensions: [AshAuthentication.TokenResource],
authorizers: [Ash.Policy.Authorizer]

policies do
policy always() do
authorize_if always()
end
end

token do
api Coruscant.Accounts
end

postgres do
table "tokens"
repo Coruscant.Repo
end
end
defmodule Coruscant.Accounts.Token do
use Ash.Resource,
data_layer: AshPostgres.DataLayer,
extensions: [AshAuthentication.TokenResource],
authorizers: [Ash.Policy.Authorizer]

policies do
policy always() do
authorize_if always()
end
end

token do
api Coruscant.Accounts
end

postgres do
table "tokens"
repo Coruscant.Repo
end
end
Still the same result in the playground.
ZachDaniel
ZachDaniel3y ago
Hmm…try removing the policy authorizer from both? Be sure to remove the extension too. Using it in code ought to be the same as graphql. Are you requesting related information in the response? Can I see the query you’re making?
csos95
csos95OP3y ago
Here's the query
mutation SignIn($name: String!, $password: String!) {
signIn(name: $name, password: $password) {
id
name
}
}
mutation SignIn($name: String!, $password: String!) {
signIn(name: $name, password: $password) {
id
name
}
}
and the variables.
{
"name": "foo",
"password": "password"
}
{
"name": "foo",
"password": "password"
}
Same result in the playground (forbidden error) and iex (no error) with the policy and extension on user and token removed.
ZachDaniel
ZachDaniel3y ago
Oh, when calling it in code can you add authorize?: true option? Wait You get a forbidden error even with all of it removed??? Something else weird is going on here
csos95
csos95OP3y ago
Yeah, I'm thinking I missed something important. I was getting the forbidden error before I added any policy stuff so I though it was defaulting to forbidden unless I had a policy to allow it.
ZachDaniel
ZachDaniel3y ago
Okay so I think this may actually be something weird in the way we display errors here or something…
csos95
csos95OP3y ago
Gitea: Git with a cup of tea
coruscant
ZachDaniel
ZachDaniel3y ago
Will take a look soon Yeah, okay so I have some info on this. The basic problem is that we were treating all forbidden errors as "policy forbidden errors" which is incorrect 🙂 So what I will do first here is ship the fix for that, and then we can take a look. For example, signing in with incorrect credentials shows this
{
"data": {
"signIn": null
},
"errors": [
{
"locations": [
{
"column": 3,
"line": 2
}
],
"message": "Something went wrong. Unique error id: `e8d2c22a-44ea-4e7a-9bee-9c4a4aba8f80`",
"path": [
"signIn"
]
}
]
}
{
"data": {
"signIn": null
},
"errors": [
{
"locations": [
{
"column": 3,
"line": 2
}
],
"message": "Something went wrong. Unique error id: `e8d2c22a-44ea-4e7a-9bee-9c4a4aba8f80`",
"path": [
"signIn"
]
}
]
}
Which is the expected thing for "unhandled" errors Then you get a log message like this:
[warning] `e8d2c22a-44ea-4e7a-9bee-9c4a4aba8f80`: AshGraphql.Error not implemented for error:

** (AshAuthentication.Errors.AuthenticationFailed) Authentication failed
(ash_authentication 3.7.3) lib/ash_authentication/errors/authentication_failed.ex:13: AshAuthentication.Errors.AuthenticationFailed.exception/1
(ash_authentication 3.7.3) lib/ash_authentication/strategies/password/sign_in_preparation.ex:55: anonymous fn/3 in AshAuthentication.Strategy.Password.SignInPreparation.prepare/3
(ash 2.5.9) lib/ash/actions/read.ex:1265: anonymous fn/2 in Ash.Actions.Read.run_after_action/2
(elixir 1.14.0) lib/enum.ex:4751: Enumerable.List.reduce/3
(elixir 1.14.0) lib/enum.ex:2514: Enum.reduce_while/3
(ash 2.5.9) lib/ash/actions/read.ex:1263: Ash.Actions.Read.run_after_action/2
(ash 2.5.9) lib/ash/actions/read.ex:975: anonymous fn/3 in Ash.Actions.Read.data_field/2
(ash 2.5.9) lib/ash/engine/engine.ex:468: anonymous fn/2 in Ash.Engine.run_iteration/1
(ash 2.5.9) lib/ash/engine/engine.ex:623: Ash.Engine.start_pending_tasks/1
(ash 2.5.9) lib/ash/engine/engine.ex:273: Ash.Engine.run_to_completion/1
(ash 2.5.9) lib/ash/engine/engine.ex:202: Ash.Engine.do_run/2
(ash 2.5.9) lib/ash/engine/engine.ex:141: Ash.Engine.run/2
(ash 2.5.9) lib/ash/actions/read.ex:170: Ash.Actions.Read.do_run/3
(ash 2.5.9) lib/ash/actions/read.ex:90: Ash.Actions.Read.run/3
(ash 2.5.9) lib/ash/api/api.ex:1005: Ash.Api.read_one/3
(coruscant 0.1.0) lib/coruscant/accounts.ex:1: Coruscant.Accounts.read_one/2
(ash_graphql 0.22.4) lib/graphql/resolver.ex:66: AshGraphql.Graphql.Resolver.resolve/2
(absinthe 1.7.0) lib/absinthe/phase/document/execution/resolution.ex:230: Absinthe.Phase.Document.Execution.Resolution.reduce_resolution/1
[warning] `e8d2c22a-44ea-4e7a-9bee-9c4a4aba8f80`: AshGraphql.Error not implemented for error:

** (AshAuthentication.Errors.AuthenticationFailed) Authentication failed
(ash_authentication 3.7.3) lib/ash_authentication/errors/authentication_failed.ex:13: AshAuthentication.Errors.AuthenticationFailed.exception/1
(ash_authentication 3.7.3) lib/ash_authentication/strategies/password/sign_in_preparation.ex:55: anonymous fn/3 in AshAuthentication.Strategy.Password.SignInPreparation.prepare/3
(ash 2.5.9) lib/ash/actions/read.ex:1265: anonymous fn/2 in Ash.Actions.Read.run_after_action/2
(elixir 1.14.0) lib/enum.ex:4751: Enumerable.List.reduce/3
(elixir 1.14.0) lib/enum.ex:2514: Enum.reduce_while/3
(ash 2.5.9) lib/ash/actions/read.ex:1263: Ash.Actions.Read.run_after_action/2
(ash 2.5.9) lib/ash/actions/read.ex:975: anonymous fn/3 in Ash.Actions.Read.data_field/2
(ash 2.5.9) lib/ash/engine/engine.ex:468: anonymous fn/2 in Ash.Engine.run_iteration/1
(ash 2.5.9) lib/ash/engine/engine.ex:623: Ash.Engine.start_pending_tasks/1
(ash 2.5.9) lib/ash/engine/engine.ex:273: Ash.Engine.run_to_completion/1
(ash 2.5.9) lib/ash/engine/engine.ex:202: Ash.Engine.do_run/2
(ash 2.5.9) lib/ash/engine/engine.ex:141: Ash.Engine.run/2
(ash 2.5.9) lib/ash/actions/read.ex:170: Ash.Actions.Read.do_run/3
(ash 2.5.9) lib/ash/actions/read.ex:90: Ash.Actions.Read.run/3
(ash 2.5.9) lib/ash/api/api.ex:1005: Ash.Api.read_one/3
(coruscant 0.1.0) lib/coruscant/accounts.ex:1: Coruscant.Accounts.read_one/2
(ash_graphql 0.22.4) lib/graphql/resolver.ex:66: AshGraphql.Graphql.Resolver.resolve/2
(absinthe 1.7.0) lib/absinthe/phase/document/execution/resolution.ex:230: Absinthe.Phase.Document.Execution.Resolution.reduce_resolution/1
So then what you could do is implement defimpl AshGraphql.Error to add a custom error message and present that to the graphql And you could also inspect the error in your handler to see what the reason for authentication failure is (we don't put it in the message, because this stuff is sensitive and we don't necessarily want errors to surface with the underlying reason) When you get a chance, can you try ash_graphql main, and implement an error handler for AshAuthentication.Errors.AuthenticationFailed to show an error message in the client and see whats going on?
csos95
csos95OP3y ago
Sure, I'll try that now.
%AshAuthentication.Errors.AuthenticationFailed{
caused_by: %{
action: :sign_in,
message: "Query returned too many users",
module: AshAuthentication.Strategy.Password.SignInPreparation,
strategy: %AshAuthentication.Strategy.Password{
identity_field: :name,
hashed_password_field: :hashed_password,
hash_provider: AshAuthentication.BcryptProvider,
confirmation_required?: true,
password_field: :password,
password_confirmation_field: :password_confirmation,
register_action_name: :register_with_password,
sign_in_action_name: :sign_in_with_password,
resettable: [],
name: :password,
provider: :password,
resource: Coruscant.Accounts.User
}
},
changeset: nil,
query: #Ash.Query<
resource: Coruscant.Accounts.User,
arguments: %{name: "foo", password: "**redacted**"},
filter: #Ash.Filter<name == "foo">,
select: [:id, :name]
>,
error_context: [],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :forbidden
}
%AshAuthentication.Errors.AuthenticationFailed{
caused_by: %{
action: :sign_in,
message: "Query returned too many users",
module: AshAuthentication.Strategy.Password.SignInPreparation,
strategy: %AshAuthentication.Strategy.Password{
identity_field: :name,
hashed_password_field: :hashed_password,
hash_provider: AshAuthentication.BcryptProvider,
confirmation_required?: true,
password_field: :password,
password_confirmation_field: :password_confirmation,
register_action_name: :register_with_password,
sign_in_action_name: :sign_in_with_password,
resettable: [],
name: :password,
provider: :password,
resource: Coruscant.Accounts.User
}
},
changeset: nil,
query: #Ash.Query<
resource: Coruscant.Accounts.User,
arguments: %{name: "foo", password: "**redacted**"},
filter: #Ash.Filter<name == "foo">,
select: [:id, :name]
>,
error_context: [],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :forbidden
}
ZachDaniel
ZachDaniel3y ago
Looks like you need an identity on user name Although I feel like it should have required that you have that...
csos95
csos95OP3y ago
I had thought that the identities part generated an index, but I guess not.
identities do
identity :unique_name, [:name]
end
identities do
identity :unique_name, [:name]
end
ZachDaniel
ZachDaniel3y ago
Well, it does Did you run the migration generator after adding the identity?
csos95
csos95OP3y ago
I think so, but I'll reset it and check again. When I do the register action with the same name it returns a null result and I see the db rollback message in the terminal.
ZachDaniel
ZachDaniel3y ago
Oh, interesting.
csos95
csos95OP3y ago
Yeah, same result with freshly generated resources/database and only running register once.
ZachDaniel
ZachDaniel3y ago
😢 that is mega strange
csos95
csos95OP3y ago
And the generated migration does have a unique index.
ZachDaniel
ZachDaniel3y ago
so there is only one user in the database? And its saying "Query returned too many users"??
csos95
csos95OP3y ago
Yeah
ZachDaniel
ZachDaniel3y ago
what okay lemme look at that code, one sec
csos95
csos95OP3y ago
I just pushed a commit with the defimpl added.
ZachDaniel
ZachDaniel3y ago
Okay, I found the issue, or at least the first issue
query, [record] when is_binary(:erlang.map_get(strategy.hashed_password_field, record))
query, [record] when is_binary(:erlang.map_get(strategy.hashed_password_field, record))
oh yeah this is a bug in ash_authentication 🙂 Should have seen this coming, its usually ash_graphql that highlights errors around not using ensure_selected to make sure the proper fields are present So because ash_graphql selects a subset of fields, the hashed_password field not being one of them, its coming back as nil (which is not necessarily a "security" bug because all that means is no one can log in)
csos95
csos95OP3y ago
Aha! Yeah, if I remove the private? true and select that field, signing in works.
ZachDaniel
ZachDaniel3y ago
I think you'd also have to select it, right?
csos95
csos95OP3y ago
Yeah
ZachDaniel
ZachDaniel3y ago
kk, makes sense this is an easy fix, but congratulations you're the first person to setup the sign in action over graphql 🙂 I wouldn't worry about it overly much, the point of Ash is that these actions/behaviors are the same internally across interfaces
csos95
csos95OP3y ago
🥳
ZachDaniel
ZachDaniel3y ago
So its not like signing in isn't vetted
csos95
csos95OP3y ago
So with that sorted out, how would I actually retrieve the token on the client? I see that the token gets put under the __metadata__ field in the user value, but the final return value is a user which has no token field that I can put it in with the resolution modifier. I tried adding a calculation field that's just an empty string and then setting the token value in the resolution modifier, but it seems like the calculation value overwrites it.
ZachDaniel
ZachDaniel3y ago
Okay, I pushed a branch up to ash_graphql to do the selection 🤔 I'll push up another thing to fix your issue on an action you can add
metadata :token, :string, ....
metadata :token, :string, ....
and AshGraphql will add that to the graphql type Although...that is made for create/update/destroy actions...lemme poke around Alright, so there is a few things. This may be an oversight in the way that we do read actions that are marked with as_mutation which is that there isn't a place to return metadata like there is in an actual mutation. This is a bit dumb, but there is an option for you in the short term I believe Clearly we need to work a bit on how this interacts with ashgraphql ```elixir defmodule GetTokenFromMetadata do use Ash.Calculation def calculate(users, , _) do Enum.map(users, fn user -> Map.get(user.metadata, :token) end) end end calculate :token, :string, GetTokenFromMetadata `` The reason that this is dumb is that it will add a token field to your user on all the other actions you have And it will be nil` on those other actions because the token metadata isn't set but should make it available to the client
csos95
csos95OP3y ago
Yup, that works for me.
ZachDaniel
ZachDaniel3y ago
Although you don't necessarily need the token on the client
csos95
csos95OP3y ago
Do you mean setting a cookie or something else?
ZachDaniel
ZachDaniel3y ago
Yeah, setting a cookie Not saying we shouldn't make it easier to get this information on the client
csos95
csos95OP3y ago
I think now my issue has been solved/worked around so I can use it. Do you want me to mark it as solved and close or wait for that ash authentication fix to be pushed?
ZachDaniel
ZachDaniel3y ago
So the new branch fixes the main issue of having to select the field right? So the only remaining issue once that fix is in is that you don't have a good way to return read-action-specific metadata via the graphql
csos95
csos95OP3y ago
Oh! I didn't check that. It's the select-fields branch, right?
ZachDaniel
ZachDaniel3y ago
select_fields I believe nope select-fields was right 😆
csos95
csos95OP3y ago
Yes, that fixed it.
ZachDaniel
ZachDaniel3y ago
🥳 In that case, if you have a few to create an issue about it that would be great I might actually suggest doing it in ash core because read actions don't support the metadata key like the other actions do
csos95
csos95OP3y ago
Should I use the bug or proposal template?
ZachDaniel
ZachDaniel3y ago
so I can add it to ash core and then add it to ash_authentication and then add it to ash_graphql to make special types for read actions that have metadata. What it would do is make those actions return something like [{result: {name: "name"}, metadata: {token: "token"}]. proposal template 👍 doesn't matter too much
csos95
csos95OP3y ago
GitHub
Add metadata attribute to read actions · Issue #494 · ash-project/a...
Is your feature request related to a problem? Please describe. I want to add a token field to a user resource in a sign-in action. The token is provided by ash_authentication and is put in the __me...
ZachDaniel
ZachDaniel3y ago
🙇

Did you find this page helpful?