lib/ash_authentication/verifier.ex

defmodule AshAuthentication.Verifier do
  @moduledoc """
  The Authentication verifier.

  Checks configuration constraints after compile.
  """

  use Spark.Dsl.Verifier
  alias AshAuthentication.Info
  alias Spark.{Dsl.Transformer, Error.DslError}
  import AshAuthentication.Utils

  @doc false
  @impl true
  @spec verify(map) ::
          :ok
          | {:error, term}
          | {:warn, String.t() | list(String.t())}
  def verify(dsl_state) do
    with {:ok, _api} <- validate_api_presence(dsl_state) do
      validate_token_resource(dsl_state)
    end
  end

  defp validate_api_presence(dsl_state) do
    with api when not is_nil(api) <- Transformer.get_option(dsl_state, [:authentication], :api),
         :ok <- assert_is_module(api),
         true <- function_exported?(api, :spark_is, 0),
         Ash.Api <- api.spark_is() do
      {:ok, api}
    else
      nil ->
        {:error,
         DslError.exception(
           path: [:authentication, :api],
           message: "An API module must be present"
         )}

      _ ->
        {:error,
         DslError.exception(
           path: [:authentication, :api],
           message: "Module is not an Ash.Api."
         )}
    end
  end

  defp validate_token_resource(dsl_state) do
    if_tokens_enabled(dsl_state, fn dsl_state ->
      with {:ok, resource} when is_truthy(resource) <-
             Info.authentication_tokens_token_resource(dsl_state),
           true <- is_atom(resource) do
        :ok
      else
        {:ok, falsy} when is_falsy(falsy) ->
          :ok

        {:error, reason} ->
          {:error, reason}

        false ->
          {:error,
           DslError.exception(
             path: [:authentication, :tokens, :token_resource],
             message: "is not a valid module name"
           )}
      end
    end)
  end

  defp if_tokens_enabled(dsl_state, validator) when is_function(validator, 1) do
    if Info.authentication_tokens_enabled?(dsl_state) do
      validator.(dsl_state)
    else
      :ok
    end
  end
end