Gaiia webhook: verify secret via query param instead of signature header
This commit is contained in:
parent
82dd75d2e9
commit
95d833874a
1 changed files with 31 additions and 4 deletions
|
|
@ -2,8 +2,13 @@ defmodule ToweropsWeb.Api.V1.GaiiaWebhookController do
|
|||
@moduledoc """
|
||||
Webhook endpoint for receiving real-time updates from Gaiia.
|
||||
|
||||
Gaiia sends unsigned webhooks via axios. We verify the organization exists
|
||||
and has an active Gaiia integration, then process the event.
|
||||
Gaiia doesn't sign webhook requests. Instead, a shared secret is verified
|
||||
via a `secret` query parameter in the webhook URL. When configuring the
|
||||
webhook in Gaiia, use:
|
||||
|
||||
https://towerops.net/api/v1/webhooks/gaiia/<org_id>?secret=<webhook_secret>
|
||||
|
||||
If no webhook_secret is configured in the integration, all requests are accepted.
|
||||
|
||||
Route: POST /api/v1/webhooks/gaiia/:organization_id
|
||||
"""
|
||||
|
|
@ -14,8 +19,9 @@ defmodule ToweropsWeb.Api.V1.GaiiaWebhookController do
|
|||
|
||||
require Logger
|
||||
|
||||
def create(conn, %{"organization_id" => organization_id}) do
|
||||
with {:ok, _integration} <- get_gaiia_integration(organization_id),
|
||||
def create(conn, %{"organization_id" => organization_id} = params) do
|
||||
with {:ok, integration} <- get_gaiia_integration(organization_id),
|
||||
:ok <- verify_secret(integration, params),
|
||||
{:ok, event} <- get_event(conn.body_params) do
|
||||
# Gaiia sends object data in "payload" field
|
||||
data = Map.get(conn.body_params, "payload", Map.get(conn.body_params, "data", %{}))
|
||||
|
|
@ -35,6 +41,24 @@ defmodule ToweropsWeb.Api.V1.GaiiaWebhookController do
|
|||
end
|
||||
end
|
||||
|
||||
defp verify_secret(integration, params) do
|
||||
case get_in(integration.credentials, ["webhook_secret"]) do
|
||||
nil ->
|
||||
# No secret configured — accept all requests
|
||||
:ok
|
||||
|
||||
"" ->
|
||||
:ok
|
||||
|
||||
expected_secret ->
|
||||
if Plug.Crypto.secure_compare(params["secret"] || "", expected_secret) do
|
||||
:ok
|
||||
else
|
||||
{:error, :invalid_secret}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp get_event(%{"eventName" => event}) when is_binary(event), do: {:ok, event}
|
||||
defp get_event(%{"event" => event}) when is_binary(event), do: {:ok, event}
|
||||
defp get_event(_), do: {:error, :missing_event}
|
||||
|
|
@ -45,6 +69,9 @@ defmodule ToweropsWeb.Api.V1.GaiiaWebhookController do
|
|||
{:error, :not_found} ->
|
||||
conn |> put_status(:not_found) |> json(%{error: "Gaiia integration not found"})
|
||||
|
||||
{:error, :invalid_secret} ->
|
||||
conn |> put_status(:unauthorized) |> json(%{error: "Invalid webhook secret"})
|
||||
|
||||
{:error, :missing_event} ->
|
||||
conn |> put_status(:bad_request) |> json(%{error: "Missing event field"})
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue