defmodule AshAuthentication.Strategy.MagicLink.Transformer do @moduledoc """ DSL transformer for magic links. """ alias Ash.Resource alias AshAuthentication.Strategy.MagicLink alias Spark.Dsl.Transformer import AshAuthentication.Utils import AshAuthentication.Validations import AshAuthentication.Strategy.Custom.Helpers @doc false @spec transform(MagicLink.t(), dsl_state) :: {:ok, MagicLink.t() | dsl_state} | {:error, any} when dsl_state: map def transform(strategy, dsl_state) do with :ok <- validate_token_generation_enabled( dsl_state, "Token generation must be enabled for magic links to work." ), strategy <- maybe_set_sign_in_action_name(strategy), strategy <- maybe_set_request_action_name(strategy), strategy <- maybe_transform_token_lifetime(strategy), {:ok, dsl_state} <- maybe_build_action( dsl_state, strategy.sign_in_action_name, &build_sign_in_action(&1, strategy) ), {:ok, dsl_state} <- maybe_build_action( dsl_state, strategy.request_action_name, &build_request_action(&1, strategy) ) do dsl_state = dsl_state |> then( ®ister_strategy_actions( [strategy.sign_in_action_name, strategy.request_action_name], &1, strategy ) ) |> put_strategy(strategy) {:ok, dsl_state} end end defp maybe_transform_token_lifetime(strategy) when is_integer(strategy.token_lifetime), do: %{strategy | token_lifetime: {strategy.token_lifetime, :minutes}} defp maybe_transform_token_lifetime(strategy), do: strategy # sobelow_skip ["DOS.StringToAtom"] defp maybe_set_sign_in_action_name(strategy) when is_nil(strategy.sign_in_action_name), do: %{strategy | sign_in_action_name: String.to_atom("sign_in_with_#{strategy.name}")} defp maybe_set_sign_in_action_name(strategy), do: strategy # sobelow_skip ["DOS.StringToAtom"] defp maybe_set_request_action_name(strategy) when is_nil(strategy.request_action_name), do: %{strategy | request_action_name: String.to_atom("request_#{strategy.name}")} defp maybe_set_request_action_name(strategy), do: strategy defp build_sign_in_action(_dsl_state, strategy) do arguments = [ Transformer.build_entity!(Resource.Dsl, [:actions, :read], :argument, name: strategy.token_param_name, type: :string, allow_nil?: false ) ] preparations = [ Transformer.build_entity!(Resource.Dsl, [:actions, :read], :prepare, preparation: MagicLink.SignInPreparation ) ] metadata = [ Transformer.build_entity!(Resource.Dsl, [:actions, :read], :metadata, name: :token, type: :string, allow_nil?: false ) ] Transformer.build_entity(Resource.Dsl, [:actions], :read, name: strategy.sign_in_action_name, arguments: arguments, preparations: preparations, metadata: metadata, get?: true ) end defp build_request_action(dsl_state, strategy) do identity_attribute = Resource.Info.attribute(dsl_state, strategy.identity_field) arguments = [ Transformer.build_entity!(Resource.Dsl, [:actions, :read], :argument, name: strategy.identity_field, type: identity_attribute.type, allow_nil?: false ) ] preparations = [ Transformer.build_entity!(Resource.Dsl, [:actions, :read], :prepare, preparation: MagicLink.RequestPreparation ) ] Transformer.build_entity(Resource.Dsl, [:actions], :read, name: strategy.request_action_name, arguments: arguments, preparations: preparations ) end end