sveltekit-topgg-webhook
v1.0.3
Published
A secure, drop-in Top.gg V1 webhook handler for SvelteKit.
Maintainers
Readme
sveltekit-topgg-webhook
A highly secure, drop-in webhook handler for receiving Top.gg V2 votes and Integrations in SvelteKit applications.
Unlike older Express-based middleware, this package is built specifically for modern Web Fetch APIs. It features built-in cryptographic signature verification (timing-safe), strict TypeScript definitions, automatic Replay Attack prevention, and native support for Top.gg's new RPC-style Integrations out of the box.
Installation
npm install sveltekit-topgg-webhookSetup
- Go to your Top.gg bot dashboard and retrieve your Webhook Secret (it should start with
whs_). - Add this secret to your SvelteKit
.envfile:TOPGG_WEBHOOK_SECRET=whs_your_secret_here - Set your webhook URL on Top.gg to point to your new endpoint (e.g.,
https://example.com/api/webhooks/topgg).
Basic Usage (Global Votes)
Create a new +server.ts file in your SvelteKit routes (e.g., src/routes/api/webhooks/topgg/+server.ts) and export the created handler:
import { createTopggWebhook } from 'sveltekit-topgg-webhook';
import { env } from '$env/dynamic/private';
export const POST = createTopggWebhook({
// 1. Pass in your global webhook secret
secret: env.TOPGG_WEBHOOK_SECRET,
// 2. (Optional) Handle successful votes
onVote: async (vote) => {
console.log(`User ${vote.user.platform_id} voted for the bot!`);
// Example: Award 500 gold to the player in your database
},
// 3. (Optional) Handle the "Test Webhook" button from the dashboard
onTest: async () => {
console.log("Test webhook received successfully!");
},
// 4. (Optional) Catch errors and unauthorized attempts
onError: async (error, traceId) => {
console.error(`Webhook Error [Trace: ${traceId}]:`, error);
}
});Advanced Usage (Top.gg Integrations)
If you are building an installable Top.gg Integration, Top.gg generates a unique webhook_secret for every single server.
This package natively supports Dynamic Secret Resolution, allowing you to securely look up the correct secret for incoming events before verifying their cryptographic signature.
import { createTopggWebhook } from 'sveltekit-topgg-webhook';
import { env } from '$env/dynamic/private';
import db from '$lib/database'; // Your custom DB logic
export const POST = createTopggWebhook({
// 1. Dynamically resolve the secret based on the incoming connection
secret: async (payload) => {
// Global events use the global secret
if (payload.type === 'integration.create' || payload.type === 'webhook.test') {
return env.TOPGG_GLOBAL_SECRET;
}
// Integration events use their unique server secret
const connectionId = payload.data?.connection_id;
if (connectionId) {
const integration = await db.integrations.findOne({ id: connectionId });
return integration?.webhook_secret || null;
}
return env.TOPGG_GLOBAL_SECRET;
},
// 2. Handle the handshake when a user installs your integration
onIntegrationCreate: async (data) => {
console.log(`Integration added by ${data.user.name}!`);
// CRITICAL: Save this unique secret to verify future requests!
await db.integrations.create({
id: data.connection_id,
webhook_secret: data.webhook_secret
});
// You MUST return this object to complete the Top.gg handshake
return {
webhook_url: "https://example.com/api/webhooks/topgg",
routes: ["vote.create"]
};
},
// 3. Handle when a user uninstalls your integration
onIntegrationDelete: async (data) => {
console.log(`Integration ${data.connection_id} was removed.`);
await db.integrations.delete({ id: data.connection_id });
},
// Handle votes specifically routed through the integration
onVote: async (vote) => {
console.log(`Vote received from integration ${vote.connection_id}`);
}
});Security Features
- Dynamic Secret Resolution: Safely parses routing IDs to fetch unique server secrets without executing unverified business logic.
- Timing-Safe Equality: Uses
node:crypto'stimingSafeEqualto prevent timing attacks when comparing HMAC signatures. - Replay Attack Protection: Automatically drops payloads with timestamps outside the acceptable tolerance window (default is 5 minutes).
- SvelteKit Native: Uses the native
Requesttext parsing to ensure the raw body is never mangled before cryptographic hashing.
License
Apache-2.0
