匿名登录
Create and use anonymous users to authenticate with Supabase
启用匿名登录 来构建应用,让用户在不需要输入电子邮件地址、密码、使用 OAuth 提供商或提供任何其他个人身份信息(PII)的情况下,也能获得认证体验。之后,当准备好时,用户可以将一种认证方式关联到他们的账户上。
匿名用户 vs 匿名密钥
调用 signInAnonymously() 会创建一个匿名用户。它的行为类似于一个永久用户,只是用户如果注销、清除浏览数据或使用其他设备,就无法访问他们的账户。
🌐 Calling signInAnonymously() creates an anonymous user. It behaves like a permanent user, except the user can't access their account if they sign out, clear browsing data, or use another device.
像永久用户一样,authenticated Postgres 角色在使用数据 API 访问你的项目时会被使用。这些用户的 JWT 会有一个 is_anonymous 声明,你可以在 RLS 策略中用它来区分。
🌐 Like permanent users, the authenticated Postgres role will be used when using the Data APIs to access your project. JWTs for these users will have an is_anonymous claim which you can use to distinguish in RLS policies.
这不同于 anon API 密钥,它不会创建用户,并且可以用于实现对你数据库的公开访问,因为它使用 anonymous Postgres 角色。
🌐 This is different from the anon API key which does not create a user and can be used to implement public access to your database as it uses the anonymous Postgres role.
可以使用匿名登录来构建:
🌐 Anonymous sign-ins can be used to build:
- 电子商务应用,比如结账前的购物车
- 全功能演示,无需收集个人信息
- 临时或一次性账户
在启用匿名登录之前,先查看你现有的 RLS 策略。匿名用户使用 authenticated 角色。为了区分匿名用户和永久用户,你的策略需要检查用户 JWT 的 is_anonymous 字段。
🌐 Review your existing RLS policies before enabling anonymous sign-ins. Anonymous users use the authenticated role. To distinguish between anonymous users and permanent users, your policies need to check the is_anonymous field of the user's JWT.
有关更多详情,请参见访问控制部分。
🌐 See the Access control section for more details.
在 Next.js 中使用动态渲染
Supabase 团队收到报告称,由于 Next.js 静态页面渲染,用户元数据在不同匿名用户之间被缓存。为了获得最佳用户体验,建议使用动态页面渲染。
🌐 The Supabase team has received reports of user metadata being cached across unique anonymous users as a result of Next.js static page rendering. For the best user experience, use dynamic page rendering.
自托管和本地开发
对于自托管,你可以使用提供的文件和环境变量来更新你的项目配置。更多细节请参考 本地开发文档。
🌐 For self-hosting, you can update your project configuration using the files and environment variables provided. See the local development docs for more details.
匿名登录 #
🌐 Sign in anonymously
调用 signInAnonymously() 方法:
🌐 Call the signInAnonymously() method:
1import { createClient } from '@supabase/supabase-js'23const supabase = createClient('https://your-project-id.supabase.co', 'sb_publishable_...')45// ---cut---6const { data, error } = await supabase.auth.signInAnonymously()把匿名用户变成永久用户 #
🌐 Convert an anonymous user to a permanent user
将匿名用户转换为永久用户需要将身份关联到该用户。这需要你在你的 Supabase 项目中启用手动关联。
🌐 Converting an anonymous user to a permanent user requires linking an identity to the user. This requires you to enable manual linking in your Supabase project.
绑定邮箱/手机号身份 #
🌐 Link an email / phone identity
你可以使用 updateUser() 方法将电子邮件或手机号身份与匿名用户关联。要为匿名用户添加密码,需要先验证用户的电子邮件或手机号。
🌐 You can use the updateUser() method to link an email or phone identity to the anonymous user. To add a password for the anonymous user, the user's email or phone number needs to be verified first.
1import { createClient } from '@supabase/supabase-js'23const supabase = createClient('https://your-project-id.supabase.co', 'sb_publishable_...')45// ---cut---6const { data: updateEmailData, error: updateEmailError } = await supabase.auth.updateUser({7 email: 'valid.email@supabase.io',8})910// verify the user's email by clicking on the email change link11// or entering the 6-digit OTP sent to the email address1213// once the user has been verified, update the password14const { data: updatePasswordData, error: updatePasswordError } = await supabase.auth.updateUser({15 password: 'password',16})关联一个 OAuth 身份 #
🌐 Link an OAuth identity
你可以使用 linkIdentity() 方法将 OAuth 身份链接到匿名用户。
🌐 You can use the linkIdentity() method to link an OAuth identity to the anonymous user.
1import { createClient } from '@supabase/supabase-js'23const supabase = createClient('https://your-project-id.supabase.co', 'sb_publishable_...')45// ---cut---6const { data, error } = await supabase.auth.linkIdentity({ provider: 'google' })访问控制 #
🌐 Access control
一个匿名用户像永久用户一样扮演 authenticated 角色。你可以使用行级别安全 (RLS) 策略,通过检查 auth.jwt() 返回的 JWT 中的 is_anonymous 声明来区分匿名用户和永久用户:
🌐 An anonymous user assumes the authenticated role like a permanent user. You can use row-level security (RLS) policies to differentiate between an anonymous user and a permanent user by checking for the is_anonymous claim in the JWT returned by auth.jwt():
1create policy "Only permanent users can post to the news feed"2on news_feed as restrictive for insert3to authenticated4with check ((select (auth.jwt()->>'is_anonymous')::boolean) is false );56create policy "Anonymous and permanent users can view the news feed"7on news_feed for select8to authenticated9using ( true );使用限制性政策
默认情况下,RLS 策略是宽松的,这意味着当多个策略同时应用时,它们会使用“或”运算符进行组合。重要的是要制定严格的策略,以确保在与其他策略结合使用时,匿名用户的检查总是被执行。请注意,单独的“严格”RLS 策略本身会失败,除非与另一个返回 true 的策略结合使用,以确保组合条件得到满足。
🌐 RLS policies are permissive by default, which means that they are combined using an "OR" operator when multiple policies are applied. It is important to construct restrictive policies to ensure that the checks for an anonymous user are always enforced when combined with other policies. Be aware that a single 'restrictive' RLS policy alone will fail unless combined with another policy that returns true, ensuring the combined condition is met.
解决身份冲突 #
🌐 Resolving identity conflicts
根据你的应用需求,当匿名用户被转换为永久用户时,可能会出现数据冲突。例如,在电子商务应用的场景下,匿名用户可以在不注册/登录的情况下将商品添加到购物车。当他们决定登录到已有账户时,你需要决定如何解决购物车中的数据冲突:
🌐 Depending on your application requirements, data conflicts can arise when an anonymous user is converted to a permanent user. For example, in the context of an e-commerce application, an anonymous user would be allowed to add items to the shopping cart without signing up / signing in. When they decide to sign-in to an existing account, you will need to decide how you want to resolve data conflicts in the shopping cart:
- 用现有账户里的商品覆盖购物车里的商品
- 用匿名用户的商品覆盖购物车里的商品
- 把购物车里的物品合并起来
将匿名用户关联到现有账户 #
🌐 Linking an anonymous user to an existing account
在某些情况下,你可能需要将匿名用户关联到现有账户,而不是创建一个新的永久账户。这一过程需要手动处理潜在的冲突。下面是一个一般的方法:
🌐 In some cases, you may need to link an anonymous user to an existing account rather than creating a new permanent account. This process requires manual handling of potential conflicts. Here's a general approach:
1// 1. Get the current session and verify the user is anonymous2const { data: anonData, error: anonError } = await supabase.auth.getSession()34if (!anonData.session?.user?.is_anonymous) {5 console.log('User is not anonymous. This flow only applies to anonymous users.')6 return7}89// 2. Attempt to update the user with the existing email10const { data: updateData, error: updateError } = await supabase.auth.updateUser({11 email: 'valid.email@supabase.io',12})1314// 3. Handle the error (since the email belongs to an existing user)15if (updateError) {16 console.log('This email belongs to an existing user. Please sign in to that account.')1718 // 4. Sign in to the existing account19 const {20 data: { user: existingUser },21 error: signInError,22 } = await supabase.auth.signInWithPassword({23 email: 'valid.email@supabase.io',24 password: 'user_password',25 })2627 if (existingUser) {28 // 5. Reassign entities tied to the anonymous user29 // This step will vary based on your specific use case and data model30 const { data: reassignData, error: reassignError } = await supabase31 .from('your_table')32 .update({ user_id: existingUser.id })33 .eq('user_id', anonData.session.user.id)3435 // 6. Implement your chosen conflict resolution strategy36 // This could involve merging data, overwriting, or other custom logic37 await resolveDataConflicts(anonData.session.user.id, existingUser.id)38 }39}4041// Helper function to resolve data conflicts (implement based on your strategy)42async function resolveDataConflicts(anonymousUserId, existingUserId) {43 // Implement your conflict resolution logic here44 // This could involve ignoring the anonymous user's metadata, overwriting the existing user's metadata, or merging the data of both the anonymous and existing user.45}滥用预防和速率限制 #
🌐 Abuse prevention and rate limits
由于匿名用户会被存储在你的数据库中,恶意用户可能滥用这个接口来大幅增加你的数据库大小。强烈建议你启用隐形 CAPTCHA 或 Cloudflare Turnstile来防止匿名登录被滥用。基于 IP 的速率限制为每小时 30 次请求,你可以在你的后台修改该限制。完整的速率限制列表可以在这里查看。
🌐 Since anonymous users are stored in your database, bad actors can abuse the endpoint to increase your database size drastically. It is strongly recommended to enable invisible CAPTCHA or Cloudflare Turnstile to prevent abuse for anonymous sign-ins. An IP-based rate limit is enforced at 30 requests per hour which can be modified in your dashboard. You can refer to the full list of rate limits here.
自动清理 #
🌐 Automatic cleanup
当前无法自动清理匿名用户。相反,你可以通过执行以下 SQL 从项目中删除匿名用户:
🌐 Automatic cleanup of anonymous users is currently not available. Instead, you can delete anonymous users from your project by running the following SQL:
1-- deletes anonymous users created more than 30 days ago2delete from auth.users3where is_anonymous is true and created_at < now() - interval '30 days';资源 #
🌐 Resources