Webhook

Webhook

プロジェクトで認証イベントが発生したときにリアルタイムHTTP通知を受信。ユーザーが登録、サインイン、またはバンされるたびにデータベースに同期したり、ウェルカムメールを送ったり、ワークフローをトリガーしたりできます。

Webhookとは?

WebhookはリアルタイムイベントのHTTPコールバックです。Authonにユーザー削除やバンを確認するためにポーリングする代わりに、Authonがイベント発生の瞬間に登録済みURLにPOSTリクエストを能動的に送信します。

text
Authonダッシュボードでユーザー削除
  → AuthonがPOST /webhooks/authonを送信
  → サーバーがイベントを受信し、DBからユーザーを削除

Webhookの仕組み

Authonプロジェクトでイベントが発生すると、APIはJSONペイロードとともに登録済みエンドポイントにPOSTリクエストを送信します。各リクエストにはv1=プレフィックス付きのX-Authon-Signatureヘッダーが含まれます — Webhookシークレットでtimestamp.bodyをHMAC-SHA256署名した値です。イベントを処理する前に必ずこの署名を検証してください。

1
Authonでイベント発生(例:ユーザー登録)
2
AuthonがWebhookシークレットでHMAC-SHA256署名
3
AuthonがエンドポイントURLにPOSTリクエスト送信
4
サーバーが署名を検証してイベントを処理
5
サーバーが受信確認のため2xxで応答

Webhookの設定

  1. 1
    移動: ダッシュボード → Webhook → Webhookを追加
  2. 2
    入力: エンドポイントURL — HTTPSで公開アクセス可能である必要あり
  3. 3
    選択: 受信したいイベント
  4. 4
    コピー: 署名シークレット — 一度しか表示されないので安全に保管
  5. 5
    設定: サーバー環境にAUTHON_WEBHOOK_SECRETを追加

イベントタイプ

イベントトリガー
user.created新しいユーザーが登録を完了
user.updatedユーザープロファイルが更新された
user.deletedユーザーアカウントが永久削除された
user.signinユーザーがサインイン(メールまたはOAuth)
user.signoutユーザーがサインアウト
user.banned管理者がユーザーをバンした
user.unbannedバンされたユーザーが復元された
session.created新しいセッションが作成(サインイン、トークン更新)
session.revokedセッションが取り消された(サインアウト、管理者強制取り消し)

ペイロード形式

すべてのWebhookペイロードは一貫したトップレベル構造を持つJSONオブジェクトです。 data フィールドにイベント固有の詳細が含まれます。

json
{
  "event": "user.created",
  "data": {
    "user": {
      "id": "usr_abc123",
      "email": "user@example.com",
      "displayName": "Jane Doe",
      "emailVerified": false,
      "isBanned": false,
      "publicMetadata": null,
      "createdAt": "2026-01-15T10:30:00.000Z",
      "updatedAt": "2026-01-15T10:30:00.000Z"
    }
  },
  "timestamp": "2026-01-15T10:30:00.000Z"
}

session.*イベントの場合、 dataフィールドには sessionオブジェクト(id、ipAddress、userAgent)も含まれます。

署名検証

各Webhookリクエストには X-Authon-Signature 形式の v1=<hex_digest>. ヘッダーが含まれます。署名は timestamp.rawBody に対してWebhookシークレットでHMAC-SHA256を計算した値です。タイミングセーフ比較で検証してください。

ヘッダー形式説明
X-Authon-Signaturev1={hmac_sha256}リクエスト送信元の検証用HMAC-SHA256署名
X-Authon-TimestampISO 8601イベント送信日時(ISO 8601)
X-Authon-Eventuser.createdイベントタイプ(例:user.created、session.revoked)
検証には常に 生ボディバッファ を使用してください。Expressの json() ミドルウェアはボディを再シリアライズし、空白が変更されて署名が壊れる可能性があります。

Node.jsの例

routes/webhooks.ts
import { createHmac, timingSafeEqual } from "crypto";
import express from "express";

const router = express.Router();

function verifySignature(
  rawBody: Buffer,
  signature: string,
  secret: string,
  timestamp: string,
): boolean {
  const payload = `${timestamp}.${rawBody.toString()}`;
  const expected = createHmac("sha256", secret)
    .update(payload)
    .digest("hex");
  const actual = signature.replace("v1=", "");

  const expectedBuf = Buffer.from(expected, "hex");
  const actualBuf = Buffer.from(actual, "hex");

  return (
    expectedBuf.length === actualBuf.length &&
    timingSafeEqual(expectedBuf, actualBuf)
  );
}

router.post(
  "/webhooks/authon",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const signature = req.headers["x-authon-signature"] as string;
    const timestamp = req.headers["x-authon-timestamp"] as string; // ISO 8601
    const eventType = req.headers["x-authon-event"] as string;
    const webhookSecret = process.env.AUTHON_WEBHOOK_SECRET!;

    if (!verifySignature(req.body, signature, webhookSecret, timestamp)) {
      return res.status(401).json({ error: "Invalid signature" });
    }

    const { event, data } = JSON.parse(req.body.toString());

    switch (eventType) {
      case "user.created":
        // Sync new user to your database
        break;
      case "user.deleted":
        // Remove user from your database
        break;
      case "user.updated":
        // Update user data
        break;
      case "user.banned":
        // Revoke app-level access
        break;
    }

    res.status(200).json({ received: true });
  }
);

export default router;

SDKの使用

@authon/node SDKはタイミングセーフ比較を処理する組み込み検証ヘルパーを提供します:

ts
import { AuthonBackend } from "@authon/node";

const authon = new AuthonBackend(process.env.AUTHON_SECRET_KEY!);

router.post(
  "/webhooks/authon",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const signature = req.headers["x-authon-signature"] as string;
    const timestamp = req.headers["x-authon-timestamp"] as string;

    let event: Record<string, unknown>;
    try {
      event = authon.webhooks.verify(
        req.body,
        signature,
        timestamp,
        process.env.AUTHON_WEBHOOK_SECRET!,
      );
    } catch {
      return res.status(401).json({ error: "Invalid signature" });
    }

    console.log("Verified event:", event.event);
    res.json({ received: true });
  }
);

リトライポリシー

エンドポイントが非2xxステータスコードを返すか 10秒以内に応答しない場合、Authonは指数バックオフで配信を再試行します。最大3回試行します。

試行遅延累積時間
初回0s
1回目リトライ1 second~1s
2回目リトライ2 seconds~3s

ユースケース

データベースへのユーザー同期
アプリのユーザーテーブルをAuthonと同期状態に保ちます。user.created、user.updated、user.deletedを受信して変更を自動反映します。
ウェルカムメールの送信
user.createdが発火したらウェルカムメールやオンボーディングシーケンスをトリガーします。イベントペイロードからユーザーのメールと表示名を活用できます。
リアルタイムのバン適用
user.bannedが発火したら、アプリレベルのアクセストークンやキャッシュエントリを即座に無効化して、ユーザーがサービスを継続利用できないようにします。

ベストプラクティス

常に署名を検証
署名検証をスキップしないでください。誰でもエンドポイントにPOSTできます — 検証はリクエストがAuthonからであることを証明します。
生ボディパースを使用
署名検証にはリクエストボディの正確な生バイトが必要です。Webhookルートでは JSONミドルウェアを避けてください。
素早く応答、非同期処理
即座に200を返し、イベントを非同期で処理してください。エンドポイントが10秒以上かかる場合、Authonはリトライします。
ハンドラーをべき等に
リトライにより同じイベントを複数回受信することがあります。イベントtimestampで重複排除してください — 処理済みイベントをDBに保存。
定期的にシークレットを更新
90日ごとにダッシュボードから新しい署名シークレットを生成。ダウンタイムなしでデプロイのAUTHON_WEBHOOK_SECRETを更新。
Authon — ユニバーサル認証プラットフォーム