会话管理
JWT Token 生命周期、自动刷新与多设备登录
Token 类型
Supabase Auth 采用双 Token 方案:
| Token | 有效期 | 用途 |
|---|---|---|
| Access Token | 1 小时 | 携带在 Authorization 头中访问受保护资源 |
| Refresh Token | 90 天 | 用于在 Access Token 过期后获取新 Token |
Token 生命周期
用户登录(邮箱 + 密码)
↓
返回 access_token (1h) + refresh_token (90d)
↓
客户端保存 Token(localStorage / Android SharedPreferences)
↓
access_token 过期
↓
SDK 自动使用 refresh_token 刷新
↓
获得新的 access_token
↓
… 循环直到 refresh_token 过期
↓
用户需要重新登录JWT Payload 结构
Access Token 解码后的关键字段:
{
"sub": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"app_metadata": {
"provider": "email",
"role": "user"
},
"aud": "authenticated",
"exp": 1678886400,
"iat": 1678800000
}其中 app_metadata.role 是角色信息,被 is_admin() / is_admin_or_editor() RPC 函数直接从 JWT 中读取,无需查库。
自动 Token 刷新
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(url, anonKey, {
auth: {
autoRefreshToken: true, // 自动刷新
persistSession: true, // 持久化到 localStorage
detectSessionInUrl: true, // 处理 OAuth/重置密码回调 URL
},
})
// 监听认证状态变化
supabase.auth.onAuthStateChange((event, session) => {
switch (event) {
case 'SIGNED_IN':
console.log('已登录')
break
case 'SIGNED_OUT':
console.log('已登出')
break
case 'TOKEN_REFRESHED':
console.log('Token 已刷新')
break
}
})val supabase = createSupabaseClient(
supabaseUrl = BuildConfig.SUPABASE_URL,
supabaseKey = BuildConfig.SUPABASE_ANON_KEY
) {
install(Auth)
}
// 在 Activity / ViewModel 中收集会话状态
lifecycleScope.launch {
supabase.auth.sessionStatus.collect { status ->
when (status) {
is SessionStatus.Authenticated -> { /* 已登录 */ }
is SessionStatus.NotAuthenticated -> { /* 未登录 */ }
is SessionStatus.LoadingFromStorage -> { /* 恢复中 */ }
is SessionStatus.NetworkError -> { /* 网络异常 */ }
}
}
}手动刷新
const { data, error } = await supabase.auth.refreshSession()
if (error) {
// refresh_token 也过期了,需要重新登录
await supabase.auth.signOut()
}登出
// 当前设备登出
await supabase.auth.signOut()
// 所有设备登出
await supabase.auth.signOut({ scope: 'global' })scope: 'global' 会使该用户所有设备的 refresh_token 失效,下次 Token 刷新时强制重新登录。