ash_authentication
ash_authentication copied to clipboard
Google authentication uid required Error
Hello.
I'm trying to implement Google authentication, but I'm getting this error:
%Ash.Error.Invalid{
errors: [
%Ash.Error.Changes.Required{
field: :uid,
type: :attribute,
resource: Core.Accounts.UserIdentity,
changeset: nil,
query: nil,
error_context: [],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :invalid
}
],
...
I tried two different approaches, directly using google
DSL and the other with oauth2
DSL. Both result in the same error.
strategy (google DSL):
strategies do
password :password do
identity_field :email
sign_in_tokens_enabled? true
resettable do
sender Senders.SendPasswordResetEmail
end
end
google :google do
client_id "12..."
redirect_uri "http://localhost:4000/"
client_secret "ab..."
base_url "https://www.googleapis.com"
identity_resource Core.Accounts.UserIdentity
end
end
action register_with_google:
create :register_with_google do
alias Actions.RegisterWithGoogle.Changes
argument :user_info, :map, allow_nil?: false
argument :oauth_tokens, :map, allow_nil?: false
upsert? true
upsert_identity :unique_email
change AshAuthentication.GenerateTokenChange
change AshAuthentication.Strategy.OAuth2.IdentityChange
change Changes.FillUserInfo
end
FillUserInfo:
defmodule Core.Accounts.User.Actions.RegisterWithGoogle.Changes.FillUserInfo do
@moduledoc false
use Ash.Resource.Change
def change(changeset, _opts, _context), do: do_change(changeset)
defp do_change(changeset) do
user_info = Ash.Changeset.get_argument(changeset, :user_info)
normalized_name = user_info[:name] |> String.downcase()
changeset
|> Ash.Changeset.change_attribute(:email, user_info[:email])
|> Ash.Changeset.change_attribute(:first_name, user_info[:given_name])
|> Ash.Changeset.change_attribute(:surname, user_info[:family_name])
|> Ash.Changeset.change_attribute(:normalized_full_name, normalized_name)
end
end
My user identity:
defmodule Core.Accounts.UserIdentity do
@moduledoc false
use Ash.Resource,
data_layer: AshPostgres.DataLayer,
extensions: [AshAuthentication.UserIdentity]
user_identity do
api Core.Accounts
user_resource Core.Accounts.User
end
postgres do
table "user_identities"
repo Core.Repo
end
end
As I said, I also tried using oauth2 and what changes is that I don't have the default attributes that I had to enter.
oauth2 :google do
client_id "12..."
redirect_uri "http://localhost:4000/"
client_secret "AB..."
base_url "https://www.googleapis.com"
authorize_url "https://accounts.google.com/o/oauth2/v2/auth"
token_url "/oauth2/v4/token"
user_url "/oauth2/v3/userinfo"
identity_resource Core.Accounts.UserIdentity
end
I would be grateful for any suggestions on how to fix this problem.
Dependency Current Latest Status
ash 2.17.17 2.17.19 Update not possible
ash_authentication 3.12.0 3.12.0 Up-to-date
ash_authentication_phoenix 1.9.0 1.9.0 Up-to-date
ash_geo 0.2.0 0.2.0 Up-to-date
ash_postgres 1.3.65 1.3.66 Update possible
ash_query_builder 0.6.3 0.6.3 Up-to-date
ash_rbac 0.4.0 0.4.0 Up-to-date
Just some more information about this error.
The issue is that the user_info
that we send as an argument is generated by the library ElixirAuthGoogle
This means that when I run this code: {:ok, user_info} = ElixirAuthGoogle.get_user_profile(token.access_token)
It will generate a map with atom keys, not string keys.
Then, it will fail inside lib/ash_authentication/user_identity/upsert_identity_change.ex:37: AshAuthentication.UserIdentity.UpsertIdentityChange.change/3
since Map.take
expects only string keys.
Would it be OK to change that line to:
uid =
user_info
# uid is a convention
# sub is supposedly from the spec
# id is from what has been seen from Google
|> Map.take(["uid", "sub", "id", :uid, :sub, :id])
|> Map.values()
|> Enum.reject(&is_nil/1)
|> List.first()
This way it doesn't matter if the map is atom or string, it will work both ways.
I think that should be fine. Can you open a PR with that?
The fix is merged in master. @drikanius can you please try again when you get a chance?
Fix: https://github.com/team-alembic/ash_authentication/pull/556