Webhook
X-Guard memberi tahu server aplikasi saat data pengguna organisasi berubah. Pasang di rincian aplikasi → panel Webhook: satu alamat https per aplikasi, pilih event, lalu simpan secret webhook (diawali xgwh_, tampil sekali). Event pengguna hanya dikirim ke aplikasi yang boleh dibuka pengguna itu.
Event
| Event | Artinya | Yang biasanya dilakukan |
|---|---|---|
user.created | Akun aktif pertama kali, termasuk undangan yang diterima | Siapkan akun lokal (opsional) |
user.updated | Nama atau email berubah | Perbarui profil lokal |
user.disabled | Ditangguhkan atau dinonaktifkan admin | Tangguhkan akun lokal dan cabut semua sesinya |
user.enabled | Aktif kembali | Pulihkan, hanya bila penangguhan berasal dari X-Guard |
user.deleted | Dihapus dari organisasi | Tangguhkan atau hapus akun lokal, cabut sesinya |
user.apps_changed | Ditambah ke ("access": "granted") atau dikeluarkan dari ("revoked") daftar akses aplikasi ini | Untuk revoked: cabut sesinya |
webhook.ping | Uji kirim dari konsol, tanpa sub | Jawab 2xx |
Kiriman
http
POST <alamat webhook>
Content-Type: application/json
User-Agent: x-guard-webhook/1
X-XGuard-Event-Id: 6f1c0d2e-… (sama dengan field id)
X-XGuard-Event-Type: user.disabled
X-XGuard-Timestamp: 1794200000
X-XGuard-Signature: sha256=<hex HMAC-SHA256(secret, timestamp + "." + body)>
{"email":"[email protected]","id":"6f1c0d2e-…","occurred_at":"2026-11-09T08:00:00.000000Z","sub":"USR_…","type":"user.disabled"}Aturan penerima
- Hitung HMAC-SHA256 atas body mentah (jangan parse lalu serialisasi ulang), dengan secret webhook sebagai kunci dan
timestamp + "." + bodysebagai isi. Bandingkan denganX-XGuard-Signaturesecara waktu-konstan. - Tolak timestamp yang selisihnya lebih dari 5 menit dari jam server (cegah pemutaran ulang).
- Simpan
idyang sudah diproses dan abaikan pengulangan, tetapi tetap jawab 2xx. subyang tidak dikenal aplikasi: abaikan, jawab 2xx.- Jawab 2xx dalam 10 detik. Selain itu X-Guard mencoba ulang dengan jeda 1 menit, 5 menit, 30 menit, 2 jam, 6 jam, dan 12 jam, sampai 24 jam sejak event terjadi, dengan
iddan body yang sama. - Event untuk pengguna yang sama dikirim berurutan: event berikutnya menunggu yang sebelumnya selesai.
Riwayat pengiriman (terkirim, menunggu, gagal, dan jawaban terakhir aplikasi) terlihat di panel Webhook. Uji kirim mengirim webhook.ping.
Verifikasi
Node.js
js
import { createHmac, timingSafeEqual } from "node:crypto";
// bodyMentah: Buffer body permintaan apa adanya (mis. express.raw({ type: "application/json" })).
export function verifikasiWebhook(secret, headers, bodyMentah) {
const ts = headers["x-xguard-timestamp"];
if (!/^\d+$/.test(ts ?? "") || Math.abs(Date.now() / 1000 - Number(ts)) > 300) throw new Error("timestamp");
const harus = Buffer.from("sha256=" + createHmac("sha256", secret).update(`${ts}.`).update(bodyMentah).digest("hex"));
const dapat = Buffer.from(headers["x-xguard-signature"] ?? "");
if (harus.length !== dapat.length || !timingSafeEqual(harus, dapat)) throw new Error("tanda tangan");
return JSON.parse(bodyMentah.toString("utf8"));
}PHP
php
$ts = $request->header('X-XGuard-Timestamp');
$harus = 'sha256='.hash_hmac('sha256', $ts.'.'.$request->getContent(), env('XGUARD_WEBHOOK_SECRET'));
abort_unless(ctype_digit((string) $ts) && abs(time() - (int) $ts) <= 300
&& hash_equals($harus, (string) $request->header('X-XGuard-Signature')), 401);
$event = json_decode($request->getContent(), true);Python
python
import hashlib, hmac, json, time
def verifikasi_webhook(secret: str, headers: dict, body: bytes) -> dict:
ts = headers.get("X-XGuard-Timestamp", "")
if not ts.isdigit() or abs(time.time() - int(ts)) > 300:
raise ValueError("timestamp")
harus = "sha256=" + hmac.new(secret.encode(), ts.encode() + b"." + body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(harus, headers.get("X-XGuard-Signature", "")):
raise ValueError("tanda tangan")
return json.loads(body)Go: xguard.WebhookHandler(secret, fn) di SDK Go. Laravel: XGuardSso::verifyWebhook di SDK Laravel. ASP Classic: webhook.asp di SDK ASP Classic.