在用户创建钩子之前
Prevent unwanted signups by inspecting and rejecting user creation requests
这个钩子在创建新用户之前运行。它允许开发者检查传入的用户对象,并可选择拒绝请求。可以用它来执行 Supabase Auth 本身不处理的自定义注册策略,比如阻止一次性邮箱域名、按地区或 IP 限制访问,或要求用户必须属于特定的邮箱域名。
🌐 This hook runs before a new user is created. It allows developers to inspect the incoming user object and optionally reject the request. Use this to enforce custom signup policies that Supabase Auth does not handle natively - such as blocking disposable email domains, restricting access by region or IP, or requiring that users belong to a specific email domain.
你可以通过 HTTP 接口或 Postgres 函数来实现这个钩子。如果钩子返回错误对象,注册会被拒绝,用户不会被创建。如果钩子成功响应(HTTP 200 或 204 且没有错误),请求就会照常进行。这样你就可以完全控制允许哪些用户注册,同时也可以灵活地在服务器端应用这个逻辑。
🌐 You can implement this hook using an HTTP endpoint or a Postgres function. If the hook returns an error object, the signup is denied and the user is not created. If the hook responds successfully (HTTP 200 or 204 with no error), the request proceeds as usual. This gives you full control over which users are allowed to register — and the flexibility to apply that logic server-side.
输入 #
🌐 Inputs
Supabase Auth 会向你的 hook 发送包含这些字段的负载:
🌐 Supabase Auth will send a payload containing these fields to your hook:
| 字段 | 类型 | 描述 |
|---|---|---|
metadata | object | 请求的元数据。包括IP地址、请求ID和钩子类型。 |
user | object | 即将创建的用户记录。与 auth.users 表的结构一致。 |
因为这个钩子在插入数据库之前立即执行,所以在钩子被调用的时候,这个用户在 Postgres 里是找不到的。
🌐 Because the hook runs immediately before insertion into the database, this user will not be found in Postgres at the time the hook is called.
1{2 "metadata": {3 "uuid": "8b34dcdd-9df1-4c10-850a-b3277c653040",4 "time": "2025-04-29T13:13:24.755552-07:00",5 "name": "before-user-created",6 "ip_address": "127.0.0.1"7 },8 "user": {9 "id": "ff7fc9ae-3b1b-4642-9241-64adb9848a03",10 "aud": "authenticated",11 "role": "",12 "email": "valid.email@supabase.com",13 "phone": "",14 "app_metadata": {15 "provider": "email",16 "providers": ["email"]17 },18 "user_metadata": {},19 "identities": [],20 "created_at": "0001-01-01T00:00:00Z",21 "updated_at": "0001-01-01T00:00:00Z",22 "is_anonymous": false23 }24}输出 #
🌐 Outputs
你的钩子必须返回一个响应,要么允许,要么阻止注册请求。
🌐 Your hook must return a response that either allows or blocks the signup request.
| 字段 | 类型 | 描述 |
|---|---|---|
error | object | (可选)返回此项以拒绝注册。包括代码、消息和可选的 HTTP 状态码。 |
返回一个空对象并带有 200 或 204 状态码可以让请求继续进行。返回一个包含 error 对象并带有 4xx 状态码的 JSON 响应会阻止请求,并将错误信息传递给客户端。更多细节请参见 错误处理文档。
🌐 Returning an empty object with a 200 or 204 status code allows the request to proceed. Returning a JSON response with an error object and a 4xx status code blocks the request and propagates the error message to the client. See the error handling documentation for more details.
允许注册 #
🌐 Allow the signup
1{}或者用 204 No Content 响应:
🌐 or with a 204 No Content response:
1HTTP/1.1 204 No Content以错误拒绝注册 #
🌐 Reject the signup with an error
1{2 "error": {3 "http_code": 400,4 "message": "Only company emails are allowed to sign up."5 }6}这个响应会阻止用户创建,并将错误信息返回给尝试注册的客户端。
🌐 This response will block the user creation and return the error message to the client that attempted signup.
示例 #
🌐 Examples
下面的每个示例都展示了如何使用 before-user-created 钩子来控制注册行为。每个用例都包括 HTTP 实现(例如使用 Edge Function)和 SQL 实现(Postgres 函数)。
🌐 Each of the following examples shows how to use the before-user-created hook to control signup behavior. Each use case includes both an HTTP implementation (e.g. using an Edge Function) and a SQL implementation (Postgres function).
只允许来自特定域名的注册,比如 supabase.com 或 example.test。其他的都拒绝。这对私有/内部应用、企业门控或者仅限邀请的测试版非常有用。
🌐 Allow signups only from specific domains like supabase.com or example.test. Reject all others. This is useful for private/internal apps, enterprise gating, or invite-only beta access.
before-user-created 钩子通过以下方式解决这个问题:
🌐 The before-user-created hook solves this by:
- 检测到用户即将被创建
- 在
user.email字段提供电子邮箱地址
在你项目的 SQL 编辑器 中运行以下代码片段。这将创建一个包含一些示例数据的 signup_email_domains 表,以及一个可以被 before-user-created 认证钩子调用的 hook_restrict_signup_by_email_domain 函数。
🌐 Run the following snippet in your project's SQL Editor. This will create a signup_email_domains table with some sample data and a hook_restrict_signup_by_email_domain function to be called by the before-user-created auth hook.
1-- Create ENUM type for domain rule classification2do $$ begin3 create type signup_email_domain_type as enum ('allow', 'deny');4exception5 when duplicate_object then null;6end $$;78-- Create the signup_email_domains table9create table if not exists public.signup_email_domains (10 id serial primary key,11 domain text not null,12 type signup_email_domain_type not null,13 reason text default null,14 created_at timestamptz not null default now(),15 updated_at timestamptz not null default now()16);1718-- Create a trigger to maintain updated_at19create or replace function update_signup_email_domains_updated_at()20returns trigger as $$21begin22 new.updated_at = now();23 return new;24end;25$$ language plpgsql;2627drop trigger if exists trg_signup_email_domains_set_updated_at on public.signup_email_domains;2829create trigger trg_signup_email_domains_set_updated_at30before update on public.signup_email_domains31for each row32execute procedure update_signup_email_domains_updated_at();3334-- Seed example data35insert into public.signup_email_domains (domain, type, reason) values36 ('supabase.com', 'allow', 'Internal signups'),37 ('gmail.com', 'deny', 'Public email provider'),38 ('yahoo.com', 'deny', 'Public email provider');3940-- Create the function41create or replace function public.hook_restrict_signup_by_email_domain(event jsonb)42returns jsonb43language plpgsql44as $$45declare46 email text;47 domain text;48 is_allowed int;49 is_denied int;50begin51 email := event->'user'->>'email';52 domain := split_part(email, '@', 2);5354 -- Check for allow match55 select count(*) into is_allowed56 from public.signup_email_domains57 where type = 'allow' and lower(domain) = lower($1);5859 if is_allowed > 0 then60 return '{}'::jsonb;61 end if;6263 -- Check for deny match64 select count(*) into is_denied65 from public.signup_email_domains66 where type = 'deny' and lower(domain) = lower($1);6768 if is_denied > 0 then69 return jsonb_build_object(70 'error', jsonb_build_object(71 'message', 'Signups from this email domain are not allowed.',72 'http_code', 40373 )74 );75 end if;7677 -- No match, allow by default78 return '{}'::jsonb;79end;80$$;8182-- Permissions83grant execute84 on function public.hook_restrict_signup_by_email_domain85 to supabase_auth_admin;8687revoke execute88 on function public.hook_restrict_signup_by_email_domain89 from authenticated, anon, public;