On this page

Auth hooks

Run your own Postgres function or HTTPS endpoint at points of the auth flow: custom token claims, your own e-mail or SMS sender, sign-up rules and lockouts.

HookRunsTypical use
Customize access tokenBefore every access token is issuedAdd roles, tenant id or plan to the JWT
Send e-mailInstead of sending an auth e-mailYour own e-mail provider or templates
Send SMSInstead of sending an SMS codeYour own SMS provider
MFA verification attemptOn each MFA code attemptLock out after too many failures
Password verification attemptOn each password sign-in attemptYour own lockout rules
Before user createdBefore a new user is storedAllow only certain e-mail domains, invite-only sign-up

#Postgres function hooks

Create a function that takes one jsonb argument and returns jsonb, then set the hook to pg-functions://postgres/<schema>/<function> in Studio → Authentication → Hooks. Saving checks that the function exists. Revoke execute from anon, authenticated and public so it can't be called through the API.

SQL
create or replace function public.custom_access_token_hook(event jsonb) returns jsonb
language plpgsql stable as $$
declare claims jsonb := event->'claims';
begin
  claims := jsonb_set(claims, '{tenant_id}', to_jsonb((select tenant_id from public.profiles where id = (event->>'user_id')::uuid)));
  return jsonb_set(event, '{claims}', claims);
end $$;
revoke execute on function public.custom_access_token_hook(jsonb) from anon, authenticated, public;
SQL
-- Before user created: reject sign-ups outside your company domain
create or replace function public.only_company(event jsonb) returns jsonb language plpgsql as $$
begin
  if event->'user'->>'email' not like '%@acme.com' then
    return jsonb_build_object('error', jsonb_build_object('http_code', 403, 'message', 'Use your acme.com address'));
  end if;
  return '{}'::jsonb;
end $$;

#HTTPS hooks

Set the hook to an https:// URL on a public host. Each request is signed with the Standard Webhooks scheme (webhook-id, webhook-timestamp, webhook-signature headers); get the secret (v1,whsec_…) with Show signing secret and verify every request. Rotate it with Rotate secret on save. Respond within a few seconds with the JSON the hook expects (for most hooks {}; to reject, {"error": {"http_code": 403, "message": "…"}}).