lib/guardian/plug/backdoor.ex

defmodule Guardian.Plug.Backdoor do
  @moduledoc """
  This plug allows you to bypass authentication in acceptance tests by passing
  the token needed to load the current resource directly to your Guardian module
  via a query string parameter.

  ## Installation

  Add the following to your Phoenix router before other Guardian plugs.

  ```
  if Mix.env() == :test do
    plug Guardian.Plug.Backdoor, module: MyApp.Guardian
  end
  plug Guardian.Plug.VerifySession
  ```

  NOTE: This plug is designed for acceptance testing and should never be added
  to a production environment.

  ## Usage

  Now that `Guardian.Plug.Backdoor` is installed, it's time to sign in. Pass
  your claims as `claims` in the query string of your route.

  ```
  conn = get(conn, "/", claims: %{sub: "User:1"})

  resource = MyApp.Guardian.Plug.current_resource(conn)
  %{"sub" => "User:1"} = MyApp.Guardian.Plug.current_claims(conn)
  ```

  When the `Guardian.Plug.Backdoor` plug runs, it fetches the resource from your
  Guardian implementation with those claims and signs in.

  Alternatively, encode your claims into a token and pass that as `token` in the
  query string instead.

  ```
  {:ok, token, _claims} = MyApp.Guardian.encode_and_sign(resource)

  conn = get(conn, "/", token: token)

  resource = MyApp.Guardian.Plug.current_resource(conn)
  ```

  ## Options

    `:module` - Your app's `Guardian` implementation module. Required.

  [hound]: https://github.com/HashNuke/hound
  """
  import Plug.Conn

  @doc false
  def init(opts) do
    Enum.into(opts, %{})
  end

  @doc false
  def call(conn, %{module: module}) do
    conn = fetch_query_params(conn)

    case resource_from_params(module, conn.params) do
      {:ok, resource, claims} ->
        module = Module.concat(module, Plug)
        module.sign_in(conn, resource, claims)

      _ ->
        conn
    end
  end

  defp resource_from_params(module, %{"token" => token}) do
    case module.resource_from_token(token) do
      {:ok, resource, claims} -> {:ok, resource, claims}
      error -> error
    end
  end

  defp resource_from_params(module, %{"claims" => claims}) do
    case module.resource_from_claims(claims) do
      {:ok, resource} -> {:ok, resource, claims}
      error -> error
    end
  end

  defp resource_from_params(_module, _params), do: :no_resource_found
end