身份认证

电子邮件认证、OTP 回调、Google 登录、会话和账号操作。

模板用一个轻量 repository 层、SessionController 和 Compose 页面包装 Supabase Auth,覆盖邮箱认证、OTP 验证、密码恢复、Google 登录和账号安全操作。

提供什么

功能状态实现
邮箱/密码登录IntegratedSupabaseAuthRemoteDataSource.signIn() + Supabase Email provider
注册 + OTP 验证IntegratedsignUp()verifyEmailOtp()resendEmailOtp()
忘记/重置密码IntegratedresetPasswordForEmail()、recovery OTP、然后 updateUser { password = ... }
修改邮箱IntegratedupdateUser { email = ... },再验证 email-change OTP
修改密码IntegratedupdateUser { password = ... }
登出其他会话Integratedauth.signOut(SignOutScope.OTHERS)
Google 登录IntegratedAndroid Credential Manager -> Google ID token -> Supabase ID-token 登录
删除账号Partial仅客户端清理部分数据并本地登出;见下面的诚实说明

关键文件

会话状态

SupabaseClientProvider 安装 Auth 插件时使用:

install(Auth) {
    scheme = "soar"
    host = "auth-callback"
}

因此 Supabase Auth 必须 allow-list soar://auth-callbackSessionController 监听 supabaseClient.auth.sessionStatus,把 SessionStatus.Authenticated 映射成 UserInfo?,并以应用级作用域暴露 currentUserisLoading 这两个 eager StateFlow

应用代码不会手写 token 持久化逻辑,而是使用 Supabase Android Auth 插件的 SettingsSessionManager,底层是 multiplatform Settings API。启动时会调用 auth.awaitInitialization(),让 auth gate 等待恢复后的会话状态。

邮箱流程

认证页面使用 feature/auth/validation/ 中的纯 Kotlin validator:

  • validateEmail() 要求非空且格式像邮箱。
  • validatePassword() 要求至少 8 位,并同时包含字母和数字。
  • validatePasswordConfirmation() 校验确认密码。
  • validateOtp() 要求 6 位数字验证码。

AuthRepository 把 Supabase 调用包装成 AppResult<Unit>。登录、注册、OTP 验证、修改密码和完成密码重置只尝试一次;重发 OTP、请求重置密码、修改邮箱、 登出其他会话和当前删除账号流程会按幂等操作处理,最多重试 3 次,每次间隔 250 ms。

Google 登录

只有设置这个 key 后才会显示 Google 登录:

GOOGLE_WEB_CLIENT_ID=your_google_oauth_web_client_id

这里要使用 Google Cloud Console 中的 OAuth Web client ID,不是 Android client ID。CredentialManagerGoogleSignInController 会创建 GetGoogleIdOption,把这个 web client ID 设置为 server client ID,加入 SHA-256 nonce,并返回 Google ID token 和原始 nonce。 SupabaseAuthRemoteDataSource.signInWithGoogle() 再通过 Supabase 的 IDToken provider、provider = Google 完成登录。

Credential Manager 的结果会被明确处理:

结果应用行为
Success(idToken, rawNonce)通过 Supabase 登录
Cancelled当作无操作
NoCredential提示设备上没有可用 Google 账号
Failure(message)显示映射后的失败信息

同时在 Supabase Auth 中启用 Google provider,并保留同一个回调:

soar://auth-callback

账号安全操作

AccountSecurityViewModel 驱动账号安全页面,可以:

  • 请求修改邮箱,并验证发送到新邮箱的 OTP。
  • 修改当前密码。
  • 使用 SignOutScope.OTHERS 登出其他会话。
  • 触发当前删除账号流程。

删除账号的诚实说明

Kotlin 模板当前没有提供 delete-account Edge Function。 SupabaseAuthRemoteDataSource.deleteAccount() 不会删除 auth.users 行。

当前实现是客户端流程:

  1. 读取当前 Supabase user id。
  2. 删除该用户的 public.todos 行。
  3. 更新同 id 的 public.profiles,把 full_namebiophonelocationavatar_url 设置为 null
  4. 调用 auth.signOut(SignOutScope.LOCAL)

这样会清掉本地登录态和部分用户数据,但 Supabase Auth 用户仍然存在。 sample-uploads/<user-id>/ 下的存储对象不会清理,device token 和 billing 行 也没有被这个客户端流程显式删除。

建议作为模板修复:把 Swift/Expo 模板中的 delete-account Edge Function 移植到 Kotlin 模板。service-role 函数可以校验调用者 JWT,删除用户数据和 Storage 对象,再调用 auth.admin.deleteUser(userId)。这也关系到 Google Play 的账号删除政策。

验证

  • 使用邮箱/密码注册并完成 6 位 OTP。
  • 登出后再用邮箱/密码登录。
  • 发起密码恢复并完成 recovery OTP 流程。
  • 添加 GOOGLE_WEB_CLIENT_ID、启用 Supabase Google Auth、重新构建,并确认 Google 按钮出现。
  • 在 Account Security 中修改邮箱和密码。
  • 在模板修复前,确认删除账号行为与本页描述完全一致。

相关页面

On this page