QueryProxy
Documentation menu

Microsoft Teams integration

Announce query requests in a Teams channel and wire approvals through an HMAC-verified action endpoint with single-use action tokens.

Updated:

The Teams integration has two halves: announcements into a channel via an incoming webhook, and an action endpoint your automation (an outgoing webhook or a Power Automate flow) can call to approve or reject.

Announcements

  1. Add an Incoming Webhook to your Teams channel and copy its URL.
  2. In QueryProxy, open ChatOps. A DBA pastes the webhook URL and sets the enabled flag; the HMAC secret (any strong random string — you’ll use the same secret for the action endpoint below) can only be set by a system administrator. Both are stored encrypted. An integration cannot be saved without a secret, so the first save is an administrator’s. The webhook URL must be https:// and its host must be on the allowlist (hooks.slack.com, *.webhook.office.com and *.logic.azure.com by default; the list is shared across chat providers; extend it with QUERYPROXY_CHAT_WEBHOOK_ALLOWED_HOSTS).

Breaking change — the signing secret is administrator-only. From this version a DBA can no longer enter or rotate it, and rotating an existing secret requires proving the current one. An HMAC proves that the caller knows the secret, not that a particular person approved: anyone who could choose the secret could sign a call approving their own request in a colleague’s name, and the audit trail would record the colleague. On an existing install, rotate the secret once as an administrator after upgrading.

Removing an integration is administrator-only for the same reason: putting it back needs a signing secret only an administrator can set. To switch the integration off, a DBA unticks the Enabled flag and saves.

New requests are posted as a card with the requester, connection, statement count, a SQL preview (cut at 400 characters; turn it off with QUERYPROXY_CHAT_INCLUDE_SQL=false) and a Review in QueryProxy link. Decisions are announced back to the channel as they happen.

Each card also carries a single-use action token for that request, in a queryproxy envelope at the top level of the payload:

{
  "queryproxy": { "request_id": 123, "action_token": "…" },
  "type": "MessageCard"
}

Teams renderers ignore the unknown key, so the token stays out of the visible card; your automation reads it there and echoes it back to the action endpoint.

The action endpoint

To approve or reject from Teams-side automation, call:

POST https://your-queryproxy-host/webhooks/teams/actions
X-QueryProxy-Timestamp: <unix seconds>
Authorization: HMAC <base64(HMAC_SHA256("{timestamp}:{raw_body}", secret))>
Content-Type: application/json
{
  "action": "approve",
  "request_id": 123,
  "actor_id": "<AAD object id>",
  "token": "<action token>",
  "reason": "optional — used for reject"
}
  • action — approve or reject.
  • request_id — the request number from the announcement card.
  • actor_id — the Azure AD object id of the approver. QueryProxy resolves it to a user through the Teams ID mapping an admin sets in Teams & Users → Manage users (an unlinked id is rejected with 422); it is never resolved by a self-declared email. The resolved user must also belong to the integration’s team (a system admin always passes this check); otherwise the call is refused with 403 before it ever reaches the policy. The usual rules still apply: they need the DBA role in the request’s team and cannot decide on their own request. As on the web, an admin may override both rules, and an admin’s approval that does so is flagged in the audit log.
  • token — required: the single-use action token from the card’s queryproxy envelope. It is bound to that one request, expires after 24 hours and is burned on the first decision (only once that decision is actually recorded, so a call the policy turns away does not use it up), so a captured call cannot be replayed and the shared secret alone cannot conjure a decision. A call without a live token (missing, expired or already spent) is refused with 422 and a deliberately vague message.
  • reason — optional for reject; defaults to “Rejected via Teams by <name>”.

Breaking change — token is mandatory. Teams automations built against earlier versions must start reading it from the card’s queryproxy envelope and sending it, or their calls will be refused. Requests announced before this version have no token at all: decide those in the web UI.

The signature is computed over "{timestamp}:{raw_body}" (the same timestamped scheme as Slack), base64-encoded, in the Authorization: HMAC … header. A Power Automate flow can compute this header directly.

The HMAC key is the secret as stored, unless the secret is valid base64: then QueryProxy decodes it and signs with the decoded bytes (the convention Teams outgoing webhooks use). Most purely alphanumeric strings are valid base64, so sign with the decoded bytes on your side — or include a character outside the base64 alphabet (such as - or !) to have the secret used as-is.

Security

  • The X-QueryProxy-Timestamp header is required and must be within ±5 minutes of server time, so a captured request cannot be replayed later.
  • An invalid or missing HMAC (or a stale/absent timestamp) returns 401 and changes nothing.
  • The action token makes every call answer one specific announcement, once. The HMAC alone proves that the caller holds the shared secret — never which person is acting — which is also why the secret is administrator-only.
  • The approver must be pre-linked through the Teams ID mapping and must be a member of the integration’s team (or a system admin) — the shared secret alone cannot act as an arbitrary user.
  • Responses are JSON ({"ok": true|false, "message": "…"}); authorization failures return 403 with the reason, an invalid token or unlinked identity returns 422, and an unknown request id or one outside the integration’s team returns 404.
  • The endpoint is rate-limited like every webhook in QueryProxy.

Full interactive Adaptive Card buttons (as with Slack) require a Teams bot registration and are on the roadmap; the action endpoint covers approvals-from-Teams today.

Arrow keys to move, Enter to open.