Last active
December 11, 2025 08:55
-
-
Save AlexandreSi/3c47f9c5bc8d651d1b7eb1332ff97310 to your computer and use it in GitHub Desktop.
Supabase Send Email hook - Signature check with Express Typescript app
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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