feat: add optional bearer token support for webhook subscriptions
Token is stored in the DB but never returned in API responses (write-only). fireWebhooks() sends Authorization: Bearer <token> header when present.
This commit is contained in:
@@ -24,10 +24,13 @@ const fireWebhooks = async (
|
|||||||
const webhooks = getWebhooksByDid(did);
|
const webhooks = getWebhooksByDid(did);
|
||||||
if (webhooks.length === 0) return;
|
if (webhooks.length === 0) return;
|
||||||
const results = await Promise.allSettled(
|
const results = await Promise.allSettled(
|
||||||
webhooks.map(({ method, url }) =>
|
webhooks.map(({ method, url, token }) =>
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
method,
|
method,
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||||
|
},
|
||||||
body: JSON.stringify(payload),
|
body: JSON.stringify(payload),
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -43,13 +43,13 @@ router.post("/notes/feed", async (ctx) => {
|
|||||||
router.post("/:did/webhooks", async (ctx) => {
|
router.post("/:did/webhooks", async (ctx) => {
|
||||||
const { did } = ctx.params;
|
const { did } = ctx.params;
|
||||||
const body = await ctx.request.body.json();
|
const body = await ctx.request.body.json();
|
||||||
const { method, url } = body ?? {};
|
const { method, url, token } = body ?? {};
|
||||||
if (!method || !url) {
|
if (!method || !url) {
|
||||||
ctx.response.status = 400;
|
ctx.response.status = 400;
|
||||||
ctx.response.body = { error: "method and url are required" };
|
ctx.response.body = { error: "method and url are required" };
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const subscription = addWebhookSubscription({ did, method, url });
|
const subscription = addWebhookSubscription({ did, method, url, token });
|
||||||
ctx.response.status = 201;
|
ctx.response.status = 201;
|
||||||
ctx.response.body = subscription;
|
ctx.response.body = subscription;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -89,20 +89,23 @@ type WebhookSubscriptionRow = {
|
|||||||
did: string;
|
did: string;
|
||||||
method: string;
|
method: string;
|
||||||
url: string;
|
url: string;
|
||||||
|
token?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const addWebhookSubscription = (
|
export const addWebhookSubscription = (
|
||||||
{ did, method, url }: Omit<WebhookSubscriptionRow, "id">,
|
{ did, method, url, token }: Omit<WebhookSubscriptionRow, "id">,
|
||||||
): WebhookSubscriptionRow => {
|
): WebhookSubscriptionRow => {
|
||||||
db.exec(
|
db.exec(
|
||||||
"INSERT INTO webhook_subscription (did, method, url) VALUES (?, ?, ?)",
|
"INSERT INTO webhook_subscription (did, method, url, token) VALUES (?, ?, ?, ?)",
|
||||||
did,
|
did,
|
||||||
method,
|
method,
|
||||||
url,
|
url,
|
||||||
|
token ?? null,
|
||||||
);
|
);
|
||||||
return db.prepare(
|
return db.prepare(
|
||||||
"SELECT id, did, method, url FROM webhook_subscription WHERE id = last_insert_rowid()",
|
"SELECT id, did, method, url FROM webhook_subscription WHERE id = last_insert_rowid()",
|
||||||
).get<WebhookSubscriptionRow>()!;
|
).get<WebhookSubscriptionRow>()!;
|
||||||
|
// Note: token is intentionally excluded from the SELECT (write-only)
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deleteWebhooksByDid = (did: string): void => {
|
export const deleteWebhooksByDid = (did: string): void => {
|
||||||
@@ -111,7 +114,7 @@ export const deleteWebhooksByDid = (did: string): void => {
|
|||||||
|
|
||||||
export const getWebhooksByDid = (did: string): WebhookSubscriptionRow[] => {
|
export const getWebhooksByDid = (did: string): WebhookSubscriptionRow[] => {
|
||||||
return db.prepare(
|
return db.prepare(
|
||||||
"SELECT id, did, method, url FROM webhook_subscription WHERE did = ? ORDER BY id DESC LIMIT 10",
|
"SELECT id, did, method, url, token FROM webhook_subscription WHERE did = ? ORDER BY id DESC LIMIT 10",
|
||||||
).all<WebhookSubscriptionRow>(did);
|
).all<WebhookSubscriptionRow>(did);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -55,4 +55,10 @@ db.exec(`
|
|||||||
ON webhook_subscription(did);
|
ON webhook_subscription(did);
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
db.exec(`ALTER TABLE webhook_subscription ADD COLUMN token TEXT;`);
|
||||||
|
} catch {
|
||||||
|
// Column already exists — no-op
|
||||||
|
}
|
||||||
|
|
||||||
db.close();
|
db.close();
|
||||||
|
|||||||
Reference in New Issue
Block a user