lib/ash_authentication/strategies/password/dsl.ex

defmodule AshAuthentication.Strategy.Password.Dsl do
  @moduledoc """
  Defines the Spark DSL entity for this strategy.
  """

  alias AshAuthentication.Strategy.Password
  alias Spark.Dsl.Entity

  @default_token_lifetime_days 3

  @doc false
  @spec dsl :: map
  def dsl do
    %Entity{
      name: :password,
      describe: "Strategy for authenticating using local resources as the source of truth.",
      examples: [
        """
        password :password do
          identity_field :email
          hashed_password_field :hashed_password
          hash_provider AshAuthentication.BcryptProvider
          confirmation_required? true
        end
        """
      ],
      args: [{:optional, :name, :password}],
      hide: [:name],
      target: Password,
      modules: [:hash_provider],
      singleton_entity_keys: [:resettable],
      schema: [
        name: [
          type: :atom,
          doc: """
          Uniquely identifies the strategy.
          """,
          required: true
        ],
        identity_field: [
          type: :atom,
          doc: """
          The name of the attribute which uniquely identifies the user.

          Usually something like `username` or `email_address`.
          """,
          default: :username
        ],
        hashed_password_field: [
          type: :atom,
          doc: """
          The name of the attribute within which to store the user's password
          once it has been hashed.
          """,
          default: :hashed_password
        ],
        hash_provider: [
          type: {:behaviour, AshAuthentication.HashProvider},
          doc: """
          A module which implements the `AshAuthentication.HashProvider`
          behaviour.

          Used to provide cryptographic hashing of passwords.
          """,
          default: AshAuthentication.BcryptProvider
        ],
        confirmation_required?: [
          type: :boolean,
          required: false,
          doc: """
          Whether a password confirmation field is required when registering or
          changing passwords.
          """,
          default: true
        ],
        register_action_accept: [
          type: {:list, :atom},
          default: [],
          doc: "A list of additional fields to be accepted in the register action."
        ],
        password_field: [
          type: :atom,
          doc: """
          The name of the argument used to collect the user's password in
          plaintext when registering, checking or changing passwords.
          """,
          default: :password
        ],
        password_confirmation_field: [
          type: :atom,
          doc: """
          The name of the argument used to confirm the user's password in
          plaintext when registering or changing passwords.
          """,
          default: :password_confirmation
        ],
        register_action_name: [
          type: :atom,
          doc: """
          The name to use for the register action.

          If not present it will be generated by prepending the strategy name
          with `register_with_`.
          """,
          required: false
        ],
        registration_enabled?: [
          type: :boolean,
          doc: """
          If you do not want new users to be able to register using this
          strategy, set this to false.
          """,
          required: false,
          default: true
        ],
        sign_in_action_name: [
          type: :atom,
          doc: """
          The name to use for the sign in action.

          If not present it will be generated by prepending the strategy name
          with `sign_in_with_`.
          """,
          required: false
        ],
        sign_in_enabled?: [
          type: :boolean,
          doc: """
          If you do not want new users to be able to sign in using this
          strategy, set this to false.
          """,
          required: false,
          default: true
        ],
        sign_in_tokens_enabled?: [
          type: :boolean,
          doc: """
          Whether or not to support generating short lived sign in tokens. Requires the resource to have
          tokens enabled. There is no drawback to supporting this, and in the future this default will
          change from `false` to `true`.

          Sign in tokens can be generated on request by setting the `:token_type` context to `:sign_in`
          when calling the sign in action. You might do this when you need to generate a short lived token
          to be exchanged for a real token using the `validate_sign_in_token` route. This is used, for example,
          by `ash_authentication_phoenix` (since 1.7) to support signing in in a liveview, and then redirecting
          with a valid token to a controller action, allowing the liveview to show invalid username/password errors.
          """,
          required: false,
          default: false
        ],
        sign_in_token_lifetime: [
          type:
            {:or,
             [
               :pos_integer,
               {:tuple, [:pos_integer, {:in, [:days, :hours, :minutes, :seconds]}]}
             ]},
          default: {60, :seconds},
          doc: """
          A lifetime for which a generated sign in token will be valid, if `sign_in_tokens_enabled?`.

          If no unit is specified, defaults to `:seconds`.
          """
        ]
      ],
      entities: [
        resettable: [
          %Entity{
            name: :resettable,
            describe: "Configure password reset options for the resource",
            target: Password.Resettable,
            schema: [
              token_lifetime: [
                type:
                  {:or,
                   [
                     :pos_integer,
                     {:tuple, [:pos_integer, {:in, [:days, :hours, :minutes, :seconds]}]}
                   ]},
                doc: """
                How long should the reset token be valid.

                If no unit is provided `:hours` is assumed.

                Defaults to #{@default_token_lifetime_days} days.
                """,
                default: {@default_token_lifetime_days, :days}
              ],
              request_password_reset_action_name: [
                type: :atom,
                doc: """
                The name to use for the action which generates a password reset token.

                If not present it will be generated by prepending the strategy name
                with `request_password_reset_with_`.
                """,
                required: false
              ],
              password_reset_action_name: [
                type: :atom,
                doc: """
                The name to use for the action which actually resets the user's
                password.

                If not present it will be generated by prepending the strategy name
                with `password_reset_with_`.
                """,
                required: false
              ],
              sender: [
                type:
                  {:spark_function_behaviour, AshAuthentication.Sender,
                   {AshAuthentication.SenderFunction, 3}},
                doc: """
                How to send the password reset instructions to the user.

                Allows you to glue sending of reset instructions to [swoosh](https://hex.pm/packages/swoosh), [ex_twilio](https://hex.pm/packages/ex_twilio) or whatever notification system is appropriate for your application.

                Accepts a module, module and opts, or a function that takes a record, reset token and options.

                See `AshAuthentication.Sender` for more information.
                """,
                required: true
              ]
            ]
          }
        ]
      ]
    }
  end
end