Define custom AshAuthentication action for creaing a user

How can I define another action apart from register_with_password for creating users? My implementation here for the register_with_password(overrides the one from AshAuthentication) action accepts an organization argument as a map, this creates a user and an organization. Now I want define another action for creating organization users(like an admin adding users to an org), this action will accept an organization_id instead, since an organization already exists and am just relating it to a user. I have tried to duplicate the register_with_password with a different name like this:

    create :register_internal_user do
      allow_nil_input [:hashed_password]
      accept [:email]

      argument :password, :string do
        sensitive? true
        constraints min_length: 8, max_length: 32
      end

      argument :password_confirmation, :string do
        sensitive? true
        constraints min_length: 8, max_length: 32
      end

      argument :user_profile, :map, allow_nil?: false
      argument :organization_id, :uuid, allow_nil?: false

      validate AshAuthentication.Strategy.Password.PasswordConfirmationValidation
      change AshAuthentication.Strategy.Password.HashPasswordChange
      change AshAuthentication.GenerateTokenChange

      change manage_relationship(:organization_id, :organization, type: :append_and_remove)
      change manage_relationship(:user_profile, type: :direct_control)
    end

action but am getting an error in the changeset:

...
errors: [
    %Ash.Error.Framework.AssumptionFailed{
      message: "Action does not correlate with an authentication strategy",
      splode: nil,
      bread_crumbs: [],
      vars: [],
      path: [],
      stacktrace: #Splode.Stacktrace<>,
      class: :framework
    }
  ]
...

How can I implement this action?

I believe what you need to do is set the strategy option on the changes/validations. For example:

It worked automatically for the register with password action because we could find the strategy from the action name.

I have tried your suggestion of adding strategy: password to changes/validations like so:

      validate {AshAuthentication.Strategy.Password.PasswordConfirmationValidation,
                strategy: :password}

      change {AshAuthentication.Strategy.Password.HashPasswordChange, strategy: :password}
      change {AshAuthentication.GenerateTokenChange, strategy: :password}

however am still getting the same error, here is the changeset:

{:error,
 %Ash.Error.Framework{
   changeset: #Ash.Changeset<
     domain: Fleetms.Accounts,
     action_type: :create,
     action: :register_internal_user,
     attributes: %{
       id: "5bbf776d-9381-4792-a73a-7b6c01119607",
       created_at: ~U[2024-04-28 15:32:33.992334Z],
       updated_at: ~U[2024-04-28 15:32:33.992334Z],
       email: #Ash.CiString<"u@example.com">
     },
     relationships: %{
       organization: [
         {[%{id: "bc3e1509-9c5a-4a5b-9a46-2f58b667ccd6"}],
          [
            ignore?: false,
            on_missing: :unrelate,
            on_match: :ignore,
            on_lookup: :relate,
            on_no_match: :error,
            eager_validate_with: false,
            authorize?: true,
            meta: [inputs_was_list?: false, id: :organization_id],
            type: :append_and_remove
          ]}
       ],
       user_profile: [
         {[%{first_name: "Jack"}],
          [
            ignore?: false,
            on_missing: :destroy,
            on_match: :update,
            on_lookup: :ignore,
            on_no_match: :create,
            eager_validate_with: false,
            authorize?: true,
            meta: [inputs_was_list?: false, id: :user_profile],
            type: :direct_control
          ]}
       ]
     },
     arguments: %{
       password: "**redacted**",
       organization_id: "bc3e1509-9c5a-4a5b-9a46-2f58b667ccd6",
       user_profile: %{first_name: "Jack"},
       password_confirmation: "**redacted**"
     },
     errors: [
       %Ash.Error.Framework.AssumptionFailed{
         message: "Action does not correlate with an authentication strategy",
         splode: Ash.Error,
         bread_crumbs: [],
         vars: [],
         path: [],
         stacktrace: #Splode.Stacktrace<>,
         class: :framework
       }
     ],
     data: #Fleetms.Accounts.User<
       user_profile: #Ash.NotLoaded<:relationship, field: :user_profile>,
       organization: #Ash.NotLoaded<:relationship, field: :organization>,
       __meta__: #Ecto.Schema.Metadata<:built, "users">,
       id: nil,
       email: nil,
       username: nil,
       created_at: nil,
       updated_at: nil,
       organization_id: nil,
       aggregates: %{},
       calculations: %{},
       ...
     >,
     valid?: false
   >,
   query: nil,
   action_input: nil,
   errors: [
     %Ash.Error.Framework.AssumptionFailed{
       message: "Action does not correlate with an authentication strategy",
       splode: Ash.Error,
       bread_crumbs: [],
       vars: [],
       path: [],
       stacktrace: #Splode.Stacktrace<>,
       class: :framework
     }
   ],
   splode: Ash.Error,
   bread_crumbs: [],
   vars: [],
   path: [],
   stacktrace: #Splode.Stacktrace<>,
   class: :framework
 }}

Ah, right sorry about that. Try adding this to your action, above the other changes/validations:

change set_context(%{strategy_name: :password})
1 Like

Thank you Zach, that worked.

1 Like