认证
接入邮箱、Apple、Google、会话存储和账号管理。
认证基于 Supabase Auth 完整集成。应用支持邮箱密码、邮箱验证码登录、注册验证、忘记/重置密码、修改邮箱/密码、账号删除,以及原生 Apple + Google 登录。
运行时结构
lib/auth.tsx 中的 AuthProvider 是应用的认证状态源。它加载初始 Supabase session,订阅 onAuthStateChange,暴露 useAuth(),并集中封装认证方法,避免各个 screen 直接调用 Supabase Auth。
AuthGate 是 guest-first 的:它不会在启动时把整个应用挡在登录页后面。公开页面可以在未登录状态渲染,受保护区域使用 SignInGate。当已登录用户进入 auth stack 时,AuthGate 会把登录流程关闭,回到发起登录的页面或首页。
会话存储
lib/supabase.ts 创建 createClient<Database>(...) 时使用平台相关的持久化层:
| 平台 | 存储 | 原因 |
|---|---|---|
| iOS / Android | ChunkedSecureStore | Supabase session 可能超过 SecureStore 单值实用限制,因此 lib/secure-store-chunked.ts 会把长值切成 1800 字符片段 |
| Web | window.localStorage | Web 构建使用浏览器存储,并关闭 URL session 检测 |
Auth token 应只保存在 Supabase client 的存储层。不要把 session 复制到 AsyncStorage、app preferences 或 analytics 属性中。
邮箱流程
Screen 位于 app/(auth)/,表单 UI 位于 components/auth/。校验使用 react-hook-form 和 lib/auth-schemas.ts 中的 Zod schema。
| 流程 | 代码路径 | Supabase 调用 |
|---|---|---|
| 密码登录 | SignInForm → signIn | signInWithPassword |
| 验证码登录 | SignInForm → sendLoginOtp / verifyLoginOtp | signInWithOtp({ shouldCreateUser: false }) → verifyOtp(type: "email") |
| 注册 | SignUpForm → signUp | 带 full_name 用户 metadata 的 signUp |
| 注册验证 | SignUpForm → verifyEmailOtp | verifyOtp(type: "email") |
| 忘记密码 | forgot-password.tsx → resetPassword | resetPasswordForEmail |
| 完成重置 | reset-password.tsx → completePasswordReset | verifyOtp(type: "recovery") → updateUser({ password }) |
| 修改邮箱 | account-security.tsx → changeEmail / verifyEmailChangeOtp | updateUser({ email }) → verifyOtp(type: "email_change") |
| 修改密码 | account-security.tsx → changePassword | updateUser({ password }) |
凭据提交和 OTP 验证不会重试,因为重试可能消耗一次性 token 或触发 provider 限流。幂等辅助调用使用 lib/network.ts 中的小型 retryAsync 包装。
Apple Sign-In
Apple 只支持原生 iOS。components/auth/social-connections.tsx 在
Platform.OS === "ios" 时懒加载 expo-apple-authentication,请求 full name
和 email scope,然后把返回的 identity token 传给:
supabase.auth.signInWithIdToken({
provider: "apple",
token: credential.identityToken,
nonce: credential.nonce,
});如果 Apple 在首次登录返回姓名字段,应用会更新 auth metadata,并 upsert
profiles.full_name。在 Android 和 Web 上不会加载该模块,按钮会提示 Apple Sign-In 不可用。
设置清单:
- 为 Apple app identifier 启用 Sign in with Apple。
- 在 Supabase Auth 中启用 Apple provider。
- capability 或 config-plugin 变更后重新构建原生应用。
Google Sign-In
Google 只支持原生 iOS/Android。应用懒加载
@react-native-google-signin/google-signin,用
EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID 和可选的
EXPO_PUBLIC_GOOGLE_IOS_CLIENT_ID 配置;Android 会在需要时检查 Play
Services,然后把 Google ID token 传给 Supabase signInWithIdToken。
创建 OAuth clients
创建 Google Cloud Web OAuth client,并添加这个 redirect URI:
https://<ref>.supabase.co/auth/v1/callback为每个要构建的 bundle identifier 创建 iOS OAuth client:
expo.soarstarter.com.dev
expo.soarstarter.com.preview
expo.soarstarter.com为每个 package name 和测试/发布签名证书 SHA-1 创建 Android OAuth client。
配置 Supabase
在 Authentication → Providers → Google 启用 Google,并设置:
Client IDs = <web-client-id>,<ios-client-id-1>,<ios-client-id-2>,...
Client Secret = <web-client-secret>
Skip nonce checks = ON把 Web client ID 放在第一位,并加入所有 Supabase 需要接受其 token audience 的原生 client ID。
设置 Expo env
EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID=<web-client-id>.apps.googleusercontent.com
EXPO_PUBLIC_GOOGLE_IOS_CLIENT_ID=<ios-client-id>.apps.googleusercontent.comenv 变更后重启 Metro。原生 identifier 或 config-plugin 行为变更后重新构建 development client。
如果当前运行时不可用,Google 按钮会显示友好的 toast,而不是让应用崩溃。共享的社交按钮映射里有 GitHub,但目前只会显示“not yet available”的占位提示。
Account & Security
app/account-security.tsx 提供已登录账号操作:
- 修改邮箱并验证新邮箱验证码。
- 修改密码。
- 登出其他 session。
- 当原生 biometrics 可用时启用或关闭 biometric lock。
- 通过
delete-accountEdge Function 删除账号。
删除账号会用已登录 JWT 调用 supabase.functions.invoke("delete-account")。函数先删除用户 rows、移除 sample-uploads/<user-id>/ 下的文件,再删除 auth user。rows 和文件先删,是为了让中途失败时用户仍保持登录状态并可重试。subscriptions 和 payments 会从 auth.users 级联删除。
如果 iOS 应用允许用户创建账号,Apple 要求应用内提供账号删除流程。请保留这个入口。
未登录功能门
受保护但位于公开应用内的功能面使用 components/sign-in-gate.tsx 的 SignInGate。它渲染本地化登录 CTA 并打开 /login,session 落地后 auth stack 会自动关闭。
验证认证
- 使用邮箱密码注册,验证邮箱验证码,并确认 auth user 和
profilesrow 存在。 - 登出后分别用密码和邮箱验证码登录。
- 跑通 forgot/reset password。
- 在原生构建中,完成 provider 设置后测试 iOS Apple,以及 iOS/Android Google。
- 删除测试账号,并确认 auth user、rows 和上传文件都被移除。
