ThunderPhone 2.0 اب لائیو ہے۔سیلف سرو، قیمت 2¢ فی منٹ سے شروع۔اعلان پڑھیں

Operations

ویب ہُک دستخطوں کی تصدیق کریں

ThunderPhone کی بھیجی گئی ہر ویب ہُک اور ٹول درخواست پر دستخط ہوتے ہیں۔ یہاں موجود طریقۂ کار کے ذریعے دستخط کی ایک بار تصدیق کریں، پھر اپنے چلائے جانے والے ہر اینڈ پوائنٹ پر یہی جانچ دوبارہ استعمال کریں۔

ہم آپ کے سرور کو بھیجنے والی ہر درخواست — ویب ہک ڈیلیوریز اور ٹول اینڈپوائنٹ انوووکیشنز — میں X-ThunderPhone-Signature ہیڈر میں ایک HMAC-SHA256 دستخط شامل ہوتا ہے۔ تصدیق ایک بار درست کریں اور اسی ہیلپر کو ہر ہینڈلر میں استعمال کریں۔

الگورتھم

  1. درخواست کی خام باڈی پڑھیں — بالکل وہی بائٹس جو ہم نے آپ کو POST کیے ہیں۔
  2. hmac_sha256(secret, body).hexdigest() کا حساب کریں۔
  3. X-ThunderPhone-Signature کے مقابلے میں مستقل وقت میں موازنہ کریں۔ (سادہ اسٹرنگ موازنہ وقت سے متعلق معلومات ظاہر کرتا ہے۔)

ہم بالکل انہی بائٹس پر دستخط کرتے ہیں جو ہم منتقل کرتے ہیں، اس لیے خام باڈی کی تصدیق ہمیشہ کام کرتی ہے۔ یہ بائٹس پے لوڈ کی کینونیکل JSON سیریلائزیشن بھی ہوتی ہیں — کلیدیں حروفِ تہجی کے مطابق ترتیب دی گئی، مختصر جداکار (بغیر اسپیس کے , اور :)، UTF-8۔ جب آپ کا فریم ورک صرف پارس شدہ JSON فراہم کرے تو یہ آپ کو دوسرا، مکمل طور پر مساوی طریقہ دیتا ہے: کینونیکل انداز میں دوبارہ سیریلائز کریں اور اس کا HMAC بنائیں۔

# Equivalent to hashing the raw body:
import json
canonical = json.dumps(payload, separators=(",", ":"), sort_keys=True).encode("utf-8")

خام باڈی کو ترجیح دیں — اس میں ایک مرحلہ کم ہے اور کچھ زبانوں میں JSON نمبروں کے راؤنڈ ٹرپ کی خصوصیات سے محفوظ رہتی ہے۔

کون سا secret؟

ماخذSecret
ویب ہک اینڈپوائنٹ (/v1/developer/webhook-endpoints)ہر اینڈپوائنٹ کا secret (48 hex حروف) جو تخلیق کے وقت ایک بار واپس کیا جاتا ہے
پرانا سنگل-URL ویب ہکہر تنظیم کا secret جو GET /v1/webhook پر واپس کیا جاتا ہے
ٹول اینڈپوائنٹ انوووکیشن (آپ کے endpoint.url پر براہِ راست کال)تنظیمی سطح کا ویب ہک secret (وہی جو پرانے سنگل-URL ویب ہک کے لیے ہے) — ہر اینڈپوائنٹ کا secret نہیں

secret کو اپنے secret manager یا env var میں محفوظ کریں — اسے کبھی commit نہ کریں۔

حوالہ جاتی نفاذ

چاروں خام درخواست باڈی کی تصدیق کرتے ہیں:

Python
import hashlib
import hmac
 
 
def verify(body: bytes, signature: str, secret: str) -> bool:
    """Constant-time HMAC-SHA256 verification."""
    expected = hmac.new(
        secret.encode("utf-8"),
        body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature or "")
Node.js
import crypto from "node:crypto";
 
export function verify(body, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(body)
    .digest("hex");
  if (!signature || expected.length !== signature.length) return false;
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature),
  );
}
Go
package webhook
 
import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
)
 
func Verify(body []byte, signature, secret string) bool {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write(body)
    expected := hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(expected), []byte(signature))
}
Ruby
require "openssl"
 
def verify(body, signature, secret)
  expected = OpenSSL::HMAC.hexdigest("SHA256", secret, body)
  Rack::Utils.secure_compare(expected, signature.to_s)
end

فریم ورک کے لیے مخصوص وائرنگ

FastAPI
from fastapi import FastAPI, HTTPException, Request
 
app = FastAPI()
 
@app.post("/thunderphone-webhook")
async def hook(request: Request):
    body = await request.body()           # raw bytes, NOT request.json()
    sig = request.headers.get("X-ThunderPhone-Signature", "")
    if not verify(body, sig, SECRET):
        raise HTTPException(status_code=401)
 
    import json
    event = json.loads(body)
    # … dispatch on event["type"] …
    return {"ok": True}
Express
import express from "express";
 
const app = express();
 
app.post(
  "/thunderphone-webhook",
  // IMPORTANT: parse as raw; do NOT use express.json() here.
  express.raw({ type: "application/json" }),
  (req, res) => {
    const sig = req.header("X-ThunderPhone-Signature") || "";
    if (!verify(req.body, sig, process.env.WEBHOOK_SECRET)) {
      return res.sendStatus(401);
    }
    const event = JSON.parse(req.body.toString("utf8"));
    // … dispatch on event.type …
    res.sendStatus(204);
  },
);
Django
import json
 
from django.http import JsonResponse, HttpResponseForbidden
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
 
 
@csrf_exempt
@require_POST
def hook(request):
    body = request.body  # raw bytes
    sig = request.headers.get("X-ThunderPhone-Signature", "")
    if not verify(body, sig, SECRET):
        return HttpResponseForbidden("invalid signature")
    event = json.loads(body)
    # … dispatch on event["type"] …
    return JsonResponse({"ok": True})

ٹول کالز کی تصدیق

جب ایجنٹ آپ کے فنکشن ٹولز میں سے کسی ایک کو براہِ راست استعمال کرتا ہے (اس ٹول کے پاس ایک endpoint ہوتا ہے)، تو درخواست آپ کے تشکیل کردہ endpoint.headers کے ساتھ ThunderPhone کے دو ہیڈرز لے کر آتی ہے:

  • X-ThunderPhone-Call-ID — جاری کال کی عددی آئی ڈی۔
  • X-ThunderPhone-Signature — عین درخواست باڈی بائٹس پر آپ کے تنظیمی سطح کے ویب ہک سیکرٹ کے ساتھ کی گئی HMAC-SHA256۔

وہی verify() ہیلپر دو فرقوں کے ساتھ بغیر تبدیلی کے کام کرتا ہے:

  1. GET / DELETE ٹولز کی کوئی باڈی نہیں ہوتی۔ آرگیومنٹس کوئری پیرامیٹرز کے طور پر منتقل ہوتے ہیں، اور دستخط خالی بائٹ اسٹرنگ پر شمار کیا جاتا ہے — یعنی verify(b"", sig, secret) (Python) یا verify(Buffer.alloc(0), sig, secret) (Node)۔ کوئری اسٹرنگ کو ہیش نہ کریں۔
  2. جن تنظیموں کے لیے لیگیسی ویب ہک تشکیل شدہ نہیں ہے، ان کے پاس تنظیمی سیکرٹ نہیں ہوتا۔ اس صورت میں ٹول کالز صرف X-ThunderPhone-Call-ID رکھتی ہیں اور دستخط کا کوئی ہیڈر نہیں ہوتا۔ سائننگ سیکرٹ حاصل کرنے کے لیے لیگیسی ویب ہک (PUT /v1/webhook) تشکیل دیں، یا endpoint.headers کے ذریعے اپنے ہی ہیڈر سے ٹول کالز کی توثیق کریں۔
@app.post("/tools/search-appointments")
async def tool(request: Request):
    body = await request.body()  # b"" for GET/DELETE tools
    sig = request.headers.get("X-ThunderPhone-Signature", "")
    call_id = request.headers.get("X-ThunderPhone-Call-ID", "")
    if not verify(body, sig, ORG_WEBHOOK_SECRET):
        raise HTTPException(status_code=401)
    args = json.loads(body)
    ...

ویب ہک-موڈ ٹول ڈسپیچ (endpoint کے بغیر ٹولز، جو آپ کے تنظیمی ویب ہک کو telephony.tool / web.tool کے طور پر پہنچائے جاتے ہیں) ایک عام دستخط شدہ ویب ہک ہے — اوپر دیا گیا معیاری طریقہ لاگو ہوتا ہے۔ درخواست کی دونوں ساختوں کے لیے فنکشن ٹولز دیکھیں۔

عام خامیاں

ڈیفالٹ فارمیٹنگ کے ساتھ دوبارہ سیریلائز کرنا

باڈی کو پارس کر کے اپنی JSON لائبریری کے ڈیفالٹس کے ساتھ دوبارہ ڈمپ کرنے سے (, / : کے بعد اسپیسز، اندراج کی ترتیب کے مطابق کلیدیں) مختلف بائٹس بنتے ہیں اور HMAC ناکام ہو جاتا ہے۔ خام باڈی کی تصدیق کریں — یا اگر آپ کو دوبارہ سیریلائز کرنا ضروری ہو تو ہماری معیاری شکل سے بالکل مطابقت رکھیں: ترتیب شدہ کلیدیں، مختصر جداکار، UTF-8۔

فریم ورک JSON کو خودکار طور پر پارس کرتا ہے

Express کا express.json() middleware باڈی اسٹریم استعمال کر لیتا ہے اور آپ خام بائٹس کھو دیتے ہیں۔ خاص طور پر webhook روٹ پر express.raw() استعمال کریں، یا pre-middleware میں خام باڈی کو بفر کریں۔ NestJS / Koa کے لیے بھی یہی صورتحال ہے — ان کی "raw body" دستاویزات دیکھیں۔

ٹائمنگ کے لحاظ سے غیر محفوظ موازنہ

JS میں expected === signature یا Python میں expected == signature ٹائمنگ کے لحاظ سے متغیر موازنے ہیں۔ بالترتیب crypto.timingSafeEqual یا hmac.compare_digest استعمال کریں۔ کارکردگی میں فرق نہ ہونے کے برابر ہے۔

ٹول اینڈ پوائنٹس کے لیے غلط secret

براہ راست ٹول اینڈ پوائنٹ کالز پر تنظیم کی سطح کا webhook secret (GET /v1/webhook) کے ساتھ دستخط ہوتے ہیں — نہ کہ /v1/developer/webhook-endpoints کے کسی فی اینڈ پوائنٹ secret کے ساتھ۔ وہی verify() فنکشن دوبارہ استعمال کریں، لیکن یقینی بنائیں کہ آپ ٹول روٹس پر اسے تنظیم کا secret فراہم کرتے ہیں۔

GET/DELETE ٹولز میں query string کو hash کرنا

باڈی کے بغیر ٹول میتھڈز کے لیے دستخط خالی بائٹ اسٹرنگ پر مشتمل ہوتا ہے، جس سے ایک ہی عالمگیر طریقہ برقرار رہتا ہے: خام درخواست باڈی پر HMAC لگائیں، خواہ وہ کچھ بھی ہو۔ URL یا query string کو hash کرنے سے کبھی مطابقت نہیں ہوگی۔

مطابقت نہ ہونے پر 401 واپس نہ کرنا

ناکام تصدیق پر 200 واپس کرنا ہینڈلر کو replay کا ہدف بناتا ہے۔ تصدیق ناکام ہونے پر ہمیشہ 2xx کے علاوہ جواب دیں۔


اگلے مراحل