Trusted Language Extension (TLE) that guards Supabase Auth signups:
- Blocks disposable email domains.
- Normalizes Gmail addresses (remove dots and + tags) so each Gmail account can sign up only once.
Objects are created in the extension schema (@extschema@) so the package is relocatable.
- Table:
@[email protected]_email_domains(domain text primary key) - Function:
@[email protected]_email(text) - Function:
@[email protected]_disposable_email_domain(text) - Function:
@[email protected]_disposable_email(text) - Hook helper:
@[email protected]_prevent_disposable_and_enforce_gmail_uniqueness(jsonb)
Fresh installs and upgrades seed the full disposable-domain blocklist automatically; no extra seed scripts required.
You can publish this package as mansueli@email_guard and install it the same way you would install other dbdev TLEs.
- dbdev CLI: https://supabase.github.io/dbdev/getting-started/
- Supabase CLI: https://supabase.com/docs/guides/cli
Make sure your local project is connected to your Supabase database (supabase link). Supabase already has the pg_tle extension installed, which is the only prerequisite.
Use dbdev to pull version 0.48.0 (or newer) into your migrations folder. Example:
dbdev add \
-o ./supabase/migrations/ \
-v 0.48.0 \
-s extensions \
package \
-n mansueli@email_guard-s extensionsinstalls into theextensionsschema (recommended on Supabase).- Adjust the output folder if your project keeps migrations elsewhere.
Push the generated migration to your project database:
supabase db pushAfter the migration runs, the extension is installed and seeded in the target schema.
In the Supabase dashboard, add a before-user-created hook of type Postgres Function pointing to the schema and function you installed. For the example above (installed in extensions):
extensions.hook_prevent_disposable_and_enforce_gmail_uniqueness
Behavior:
- Disposable domains trigger a 403 with a helpful error message.
- Gmail/Googlemail addresses normalize to
gmail.comwith dots removed and+tagsstripped; if a normalized Gmail already exists inauth.users, the hook raises a 409. - Signups without an email (e.g., phone) are passed through unchanged.
This repo ships an automated workflow that runs every Monday at 12:00 UTC:
- Fetches the upstream disposable-email list.
- If there are changes, bumps the minor version, creates the upgrade script, and commits both the new base version and control-file update.
- Automatically publishes the updated package to database.dev using the
dbdevCLI.
To enable automated publishing, add the following secret to the repository (Settings → Secrets and variables → Actions → New repository secret):
| Secret name | Description |
|---|---|
DBDEV_TOKEN |
Your database.dev token (found in your account settings). |
SUPABASE_ANON_KEY |
Your Supabase anon key, used to authenticate the dbdev registry API. |
Without these secrets the workflow will still commit and push blocklist updates, but the dbdev publish step will fail.
To publish manually:
dbdev login # paste your database.dev token when prompted
dbdev publish --path email_guardUpgrading your database to the new version pulls in the refreshed blocklist automatically.
-- Check if an email domain is disposable
select extensions.is_disposable_email('[email protected]');
-- Normalize a Gmail address
select extensions.normalize_email('[email protected]');
-- Simulate hook behavior
select extensions.hook_prevent_disposable_and_enforce_gmail_uniqueness(
'{"user": {"email": "[email protected]"}}'::jsonb
);Replace extensions with the schema you chose if different.
- The hook runs before the user is inserted, so only existing users are checked for Gmail duplicates.
- Domain matching walks up parent domains (
sub.mailinator.commatchesmailinator.com). - The extension is relocatable; you can install it under any schema and reference it in the hook configuration.