defmodule ToweropsWeb.Plugs.WebhookAuth do @moduledoc """ Plug for authenticating webhook requests using a shared secret. Expects the Authorization header to contain a Bearer token matching the configured `agent_webhook_secret`. Returns 401 Unauthorized if: - No Authorization header is present - Token does not match the configured secret """ import Phoenix.Controller, only: [json: 2] import Plug.Conn def init(opts), do: opts def call(conn, _opts) do case get_req_header(conn, "authorization") do ["Bearer " <> token] -> verify_secret(conn, token) _ -> conn |> put_status(:unauthorized) |> json(%{error: "Missing or invalid Authorization header"}) |> halt() end end defp verify_secret(conn, token) do secret = Application.get_env(:towerops, :agent_webhook_secret) cond do is_nil(secret) or secret == "" -> require Logger Logger.error("agent_webhook_secret not configured — rejecting webhook request") conn |> put_status(:internal_server_error) |> json(%{error: "Webhook authentication not configured"}) |> halt() Plug.Crypto.secure_compare(token, secret) -> conn true -> conn |> put_status(:unauthorized) |> json(%{error: "Invalid webhook secret"}) |> halt() end end end