lib/ash_authentication/strategies/magic_link/plug.ex

defmodule AshAuthentication.Strategy.MagicLink.Plug do
  @moduledoc """
  Plugs for the magic link strategy.

  Handles requests and sign-ins.
  """

  alias AshAuthentication.{Info, Strategy, Strategy.MagicLink}
  alias Plug.Conn
  import Ash.PlugHelpers, only: [get_actor: 1, get_tenant: 1]
  import AshAuthentication.Plug.Helpers, only: [store_authentication_result: 2]

  @doc """
  Handle a request for a magic link.

  Retrieves form parameters from nested within the subject name, eg:

  ```
  %{
    "user" => %{
      "email" => "marty@mcfly.me"
    }
  }
  ```
  """
  @spec request(Conn.t(), MagicLink.t()) :: Conn.t()
  def request(conn, strategy) do
    params = subject_params(conn, strategy)
    opts = opts(conn)
    result = Strategy.action(strategy, :request, params, opts)
    store_authentication_result(conn, result)
  end

  @doc """
  Sign in via magic link.
  """
  @spec sign_in(Conn.t(), MagicLink.t()) :: Conn.t()
  def sign_in(conn, strategy) do
    params =
      conn.params
      |> Map.take([to_string(strategy.token_param_name)])

    opts = opts(conn)
    result = Strategy.action(strategy, :sign_in, params, opts)
    store_authentication_result(conn, result)
  end

  defp subject_params(conn, strategy) do
    subject_name =
      strategy.resource
      |> Info.authentication_subject_name!()
      |> to_string()

    Map.get(conn.params, subject_name, %{})
  end

  defp opts(conn) do
    [actor: get_actor(conn), tenant: get_tenant(conn)]
    |> Enum.reject(&is_nil(elem(&1, 1)))
  end
end