Custom plug not being called

I have a custom plug which does not appear to be called in its pipeline. The plug DxAppRcWeb.CheckAuth is meant to be my auth solution for a specific endpoint on my API. None of the IO is triggering in my console for requests to this endpoint. The API is otherwise responding appropriately to graphql calls on this endpoint.

Router File:

defmodule DxAppRcWeb.Router do
  use DxAppRcWeb, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, {DxAppRcWeb.Layouts, :root}
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  scope "/", DxAppRcWeb do
    pipe_through :browser

    live "/", HomeLive
    live "/organizations", OrganizationLive
    live "/create-organization", CreateOrganizationLive
  end

  pipeline :auth do
    plug :accepts, ["json"]
    forward "/auth", DxAppRcWeb.AuthWrapper, schema: DxAppRcWeb.AuthSchema
  end

  pipeline :api do
    plug :accepts, ["json"]
    plug DxAppRcWeb.CheckAuth

    forward "/api", Absinthe.Plug, schema: DxAppRcWeb.Schema
  end
end

Auth Module:

defmodule DxAppRcWeb.CheckAuth do
  import Plug.Conn

  alias DxAppRc.Auth.MockCheckAuth

  def init(_default), do: IO.puts("Auth Plug Init")

  def call(conn, opts) do
    IO.puts("Calling Auth Plug...")
    check_auth(conn, opts)
  end

  def check_auth(conn, _opts) do
    IO.puts("Calling Check Auth...")
    auth_token = conn |> get_req_header("authorization")
    IO.inspect(auth_token, label: "Auth Token")
    validate_auth_token(conn, auth_token)
  end

  # ... Other business logic
end

Any ideas what could be going wrong? Thanks!

This is the most confusing part to me, because I don’t see any pipe_through statements using :api or :auth at all, but Absinthe.Plug is in pipeline :api. :thinking: Maybe something important got trimmed when posting the code?

1 Like

I took this setup from an Absinthe tutorial somewhere - I’ll try to hunt it down.

Just to check, I added the following code:


  scope "/api", DxAppRcWeb do
    pipe_through :api
  end

The API worked still, but still experienced the same problem with auth.

To expand on what @al2o3cr was alluding to, routes e.g. forward, get, etc. generally go inside of scope blocks and not pipeline blocks. Here’s an example from the docs: Routing — Phoenix v1.7.1.

pipeline :browser do
  ...
end

pipeline :auth do
  plug HelloWeb.Authentication
end

scope "/" do
  pipe_through [:browser]

  get "/reviews", PostController, :index
  get "/reviews/:id", PostController, :show
end

scope "/" do
  pipe_through [:browser, :auth]

  get "/reviews/new", PostController, :new
  post "/reviews", PostController, :create
end

So maybe something like this is what you intended:

  pipeline :api do
    plug :accepts, ["json"]
  end

  pipeline :auth do
    plug DxAppRcWeb.CheckAuth
  end

  scope "/" do
    pipe_through [:api, :auth]
    forward "/api", Absinthe.Plug, schema: DxAppRcWeb.Schema
  end

  scope "/" do
    pipe_through [:api]
    forward "/auth", DxAppRcWeb.AuthWrapper, schema: DxAppRcWeb.AuthSchema
  end

Thanks - this solved my problem 100%

Actually, I spoke to soon.

When I configure like so:

scope "/", DxAppRcWeb do
    pipe_through :api
    forward "/api", Absinthe.Plug, schema: DxAppRcWeb.Schema
  end

  pipeline :api do
    plug :accepts, ["json"]
    plug DxAppRcWeb.CheckAuth   
    
  end

The auth plug is utilized, but the Absinthe Plug does not initialize.

When I configure like this:

scope "/", DxAppRcWeb do
    pipe_through :api

  end

  pipeline :api do
    plug :accepts, ["json"]
    plug DxAppRcWeb.CheckAuth   
    forward "/api", Absinthe.Plug, schema: DxAppRcWeb.Schema   
  end

The Absinthe endpoint works, but it does not pipe through the auth plug.

Like I mentioned above, forward shouldn’t be inside a pipeline block and won’t actually be protected by the plugs that precede it.

Does your CheckAuth plug return a conn? All plugs need to return a conn.

I hear your statement. I’m not saying that your statement wrong, I’m just really confused as to why this isn’t working the way described.

Below is my plug in its entirety:

defmodule DxAppRcWeb.CheckAuth do
  import Plug.Conn

  alias DxAppRc.Auth.MockCheckAuth

  def init(_default), do: IO.puts("Auth Plug Init")

  def call(conn, opts) do
    IO.puts("Calling Auth Plug...")
    check_auth(conn, opts)
  end

  def check_auth(conn, _opts) do
    IO.puts("Calling Check Auth...")
    auth_token = conn |> get_req_header("authorization")
    IO.inspect(auth_token, label: "Auth Token")
    validate_auth_token(conn, auth_token)
  end

  defp validate_auth_token(conn, token) when is_nil(token), do: not_validated(conn)

  defp validate_auth_token(conn, token) do
    case MockCheckAuth.mock_check_auth(token) do
      nil -> not_validated(conn)
      _ -> conn
    end
  end

  defp not_validated(conn), do: send_resp(conn, 401, "Authentication invalid.")
end

I have validated that when the validate logic fails, it sends the 401 response.

Also yes - it is sending a connection back (verifying with inspect).

Also, the error is:

 ** (UndefinedFunctionError) function DxAppRcWeb.Absinthe.Plug.init/1 is undefined (module DxAppRcWeb.Absinthe.Plug is not available)
        DxAppRcWeb.Absinthe.Plug.init([schema: DxAppRcWeb.Schema])
        (phoenix 1.7.1) lib/phoenix/router/route.ex:42: Phoenix.Router.Route.call/2
        (phoenix 1.7.1) lib/phoenix/router.ex:425: Phoenix.Router.__call__/5
        (dx_app_rc 0.1.0) lib/dx_app_rc_web/endpoint.ex:1: DxAppRcWeb.Endpoint.plug_builder_call/2

The Absinthe Plug isn’t building properly. This is confusing to me, as when I include the forward statement in the pipeline, this error does not happen and the GraphQL endpoint works as it should. This tells me that my schema is good, that my dependencies are good, and that there is some level of configuration is proper.

Hmm, it looks like it’s forwarding to the wrong module because of the scope. I’m guessing it needs to call Absinthe.Plug and not DxAppRcWeb.Absinthe.Plug. So maybe try something like this?

  scope "/", Absinthe do
    pipe_through :api
    forward "/api", Plug, schema: DxAppRcWeb.Schema
  end

  pipeline :api do
    plug :accepts, ["json"]
    plug DxAppRcWeb.CheckAuth   
  end

Ok - I figured it out. It’s a namespace problem. Changing it to the code below works:

  scope "/api" do
    pipe_through :api
    forward "/", Absinthe.Plug, schema: DxAppRcWeb.Schema
  end

Thank you for your help!

Ah yes - we found the answer at the same time!

Thank you for your patience - I definitely need to spend more time going through the Phoenix docs.

1 Like