Refactor webhook signature verification process
The signature verification process for webhooks has been abstracted. Instead of performing the check directly in the handleWebhook method, it has been moved to a separate service class: PostgresDatabaseWebhookVerifierService. This refactor improves modularity and enables extending or replacing the verification logic more comfortably. The WEBHOOK_SENDER_PROVIDER environment variable is used to determine which verifier service to use.
This commit is contained in:
@@ -5,11 +5,12 @@ import { getSupabaseRouteHandlerClient } from '@kit/supabase/route-handler-clien
|
||||
|
||||
import { RecordChange, Tables } from '../record-change.type';
|
||||
import { DatabaseWebhookRouterService } from './database-webhook-router.service';
|
||||
import { getDatabaseWebhookVerifier } from './verifier';
|
||||
|
||||
export class DatabaseWebhookHandlerService {
|
||||
private readonly namespace = 'database-webhook-handler';
|
||||
|
||||
async handleWebhook(request: Request, webhooksSecret: string) {
|
||||
async handleWebhook(request: Request) {
|
||||
const logger = await getLogger();
|
||||
|
||||
const json = await request.clone().json();
|
||||
@@ -25,7 +26,9 @@ export class DatabaseWebhookHandlerService {
|
||||
);
|
||||
|
||||
// check if the signature is valid
|
||||
this.assertSignatureIsAuthentic(request, webhooksSecret);
|
||||
const verifier = await getDatabaseWebhookVerifier();
|
||||
|
||||
await verifier.verifySignatureOrThrow(request);
|
||||
|
||||
// all good, handle the webhook
|
||||
|
||||
@@ -64,12 +67,4 @@ export class DatabaseWebhookHandlerService {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private assertSignatureIsAuthentic(request: Request, webhooksSecret: string) {
|
||||
const header = request.headers.get('X-Supabase-Event-Signature');
|
||||
|
||||
if (header !== webhooksSecret) {
|
||||
throw new Error('Invalid signature');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export abstract class DatabaseWebhookVerifierService {
|
||||
abstract verifySignatureOrThrow(request: Request): Promise<boolean>;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
const WEBHOOK_SENDER_PROVIDER =
|
||||
process.env.WEBHOOK_SENDER_PROVIDER ?? 'postgres';
|
||||
|
||||
export async function getDatabaseWebhookVerifier() {
|
||||
switch (WEBHOOK_SENDER_PROVIDER) {
|
||||
case 'postgres': {
|
||||
const { PostgresDatabaseWebhookVerifierService } = await import(
|
||||
'./postgres-database-webhook-verifier.service'
|
||||
);
|
||||
|
||||
return new PostgresDatabaseWebhookVerifierService();
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(
|
||||
`Invalid WEBHOOK_SENDER_PROVIDER: ${WEBHOOK_SENDER_PROVIDER}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { DatabaseWebhookVerifierService } from './database-webhook-verifier.service';
|
||||
|
||||
const webhooksSecret = z
|
||||
.string({
|
||||
description: `The secret used to verify the webhook signature`,
|
||||
required_error: `Provide the variable SUPABASE_DB_WEBHOOK_SECRET. This is used to authenticate the webhook event from Supabase.`,
|
||||
})
|
||||
.min(1)
|
||||
.parse(process.env.SUPABASE_DB_WEBHOOK_SECRET);
|
||||
|
||||
export class PostgresDatabaseWebhookVerifierService
|
||||
implements DatabaseWebhookVerifierService
|
||||
{
|
||||
verifySignatureOrThrow(request: Request) {
|
||||
const header = request.headers.get('X-Supabase-Event-Signature');
|
||||
|
||||
if (header !== webhooksSecret) {
|
||||
throw new Error('Invalid signature');
|
||||
}
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user