Logo花火漫画开发文档

会话管理

JWT Token 生命周期、自动刷新与多设备登录

Token 类型

Supabase Auth 采用双 Token 方案:

Token有效期用途
Access Token1 小时携带在 Authorization 头中访问受保护资源
Refresh Token90 天用于在 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 刷新时强制重新登录。

On this page