Skip to content

Instantly share code, notes, and snippets.

@AlexandreSi
Last active December 11, 2025 08:55
Show Gist options
  • Select an option

  • Save AlexandreSi/3c47f9c5bc8d651d1b7eb1332ff97310 to your computer and use it in GitHub Desktop.

Select an option

Save AlexandreSi/3c47f9c5bc8d651d1b7eb1332ff97310 to your computer and use it in GitHub Desktop.
Supabase Send Email hook - Signature check with Express Typescript app
import cors from 'cors';
import express from 'express';
import helmet from 'helmet';
import morgan from 'morgan';
const app = express();
app.use(morgan('dev'));
app.use(helmet());
app.use(cors());
app.use(
'/api/v1/auth/email',
// Ensure that req.body is a raw text.
express.text({ type: ['*/text', 'application/json'] }),
// Keep this router above the json middleware to be able to provide the raw body to
// the Signature checking method.
authEmailRouter,
);
// Other routers below.
export default app;
import { Webhook } from 'standardwebhooks';
import express, { Request } from 'express';
import { env } from '../../env';
const router = express.Router({ mergeParams: true });
const checkSignature = async (req: Request) => {
const bodyAsText = await req.body;
const webhook = new Webhook(
env.SUPABASE_EMAIL_WEBHOOK_SECRET.replace('v1,whsec_', ''),
);
try {
const parsedData = webhook.verify(
bodyAsText,
// @ts-expect-error - Not important.
req.headers,
);
return parsedData;
} catch {
return null;
}
};
router.post('/webhook', async (req, res) => {
const payload = await checkSignature(req);
if (!payload) {
return res
.status(403)
.setHeader('Content-Type', 'application/json')
.json({ error: 'Unauthorized' })
.end();
}
// TODO: Send email.
return res
.status(200)
.setHeader('Content-Type', 'application/json')
.json({ message: 'OK' })
.end();
});
export default router;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment