认证

用户会话


Supabase Auth 为您提供了对用户会话的细粒度控制。

某些对安全性要求较高的应用,或需要符合 SOC 2、HIPAA、PCI-DSS 或 ISO27000 标准的应用,需要额外的会话控制机制来强制执行超时或提供额外的安全保障。Supabase Auth 让构建合规应用变得简单。

什么是会话?

当用户登录时就会创建一个会话。默认情况下,会话会无限期持续,并且用户可以在任意数量的设备上拥有无限数量的活跃会话。

会话由 Supabase Auth 的访问令牌(以 JWT 形式存在)和刷新令牌(一个唯一字符串)表示。

访问令牌设计为短期有效,通常在 5 分钟到 1 小时之间,而刷新令牌永不过期但只能使用一次。您只能使用刷新令牌交换一次新的访问令牌和刷新令牌对。

这个过程称为会话刷新

根据配置,会话会在以下情况下终止:

  • 用户点击退出登录
  • 用户更改密码或执行安全敏感操作
  • 因不活跃而超时
  • 达到最大生命周期
  • 用户在另一台设备上登录

访问令牌 (JWT) 声明

每个访问令牌都包含一个 session_id 声明,这是一个 UUID,用于唯一标识用户的会话。您可以将此 ID 与 auth.sessions 表的主键关联。

初始化会话

当用户登录时会初始化会话。会话存储在 auth.sessions 表中,您的应用应该会收到访问令牌和刷新令牌。

有两种初始化会话并接收令牌的流程:

限制会话生命周期和每个用户允许的会话数量

Supabase Auth 可以配置为限制用户会话的生命周期。默认情况下,所有会话都会保持活动状态,直到用户登出或执行其他终止会话的操作。

在某些应用中,出于安全考虑,需要确保用户频繁重新认证,或防止会话在设备上保持活动状态过长时间。

有三种方式可以限制会话生命周期:

  • 时间盒会话:在固定时间后终止会话
  • 设置不活动超时:在超时时间内未刷新的会话将被终止
  • 强制单会话模式:仅保留最近活动的会话

要确保用户定期重新认证,您可以在项目的认证设置中为时间盒用户会话选项设置正值。

要确保会话在不活动一段时间后过期,您可以在认证设置中为不活动超时选项设置正持续时间。

您还可以强制每个用户在每个设备或浏览器上只能有一个活动会话。启用此功能后,最近登录的会话将保持活动状态,其他会话将被终止。通过认证设置中的_单用户会话_选项启用此功能。

更改这些设置时,会话不会立即被主动销毁,而是在下次刷新会话时强制执行检查。这可能会让开发者感到困惑,因为实际会话持续时间是配置的超时时间加上JWT过期时间。对于单会话模式,效果只会在JWT过期时间间隔时显现。请根据需求调整此设置。我们不建议将JWT过期时间设置为低于5分钟。

此外,过期的会话会在24小时后从数据库中逐步删除,这样可以防止意外对项目造成高负载,并允许您有一定自由度撤销更改而不会对所有用户造成不利影响。

常见问题解答

访问令牌(JWT)的推荐过期时间是多少?

大多数应用应使用默认的1小时过期时间。您可以在项目的认证设置中的"高级设置"部分自定义此值。

出于安全考虑,通常不建议设置超过1小时的过期时间,但在某些特定情况下可能有意义。

大多数情况下不应使用低于5分钟(特别是低于2分钟)的值,因为:

  • 过期时间越短,刷新令牌的使用频率就越高,这会增加认证服务器的负载
  • 时间不是绝对的。服务器之间可能存在数十秒的时钟不同步,而笔记本电脑、台式机或移动设备等用户设备的时钟偏差可能达到数分钟甚至数小时。过短的过期时间可能因时钟偏差导致难以调试的错误
  • Supabase客户端库总是尝试提前刷新会话,如果过期时间太短就无法实现
  • 访问令牌的有效期通常应至少覆盖您应用中最长的请求处理时间,这有助于避免访问令牌在处理过程中失效的问题

什么是刷新令牌重用检测及其防护作用?

当用户持续使用您的应用程序时,刷新令牌会不断被交换为新的访问令牌。

基本原则是刷新令牌只能使用一次。但严格执行这一规则可能会导致某些问题出现。为了防止用户会话意外提前终止,设计上存在两个例外情况:

  • 刷新令牌可以在定义的重用间隔期内多次使用(默认10秒,不建议修改此值)。这一例外适用于以下合法场景:
    • 使用服务器端渲染时,同一个刷新令牌需要在服务器和稍后的客户端上重复使用
    • 为刷新令牌请求的序列化访问可能存在的bug或问题留出缓冲空间
  • 如果用户当前会话的活动刷新令牌的父令牌被使用,系统将返回活动令牌。这一例外解决了重要且常见的情况:
    • 浏览器、移动/桌面应用乃至某些服务器等所有客户端本质上都因网络问题存在不可靠性。请求发送并不代表客户端已接收或处理响应。
    • 若刷新令牌在使用一次后被撤销,而客户端未收到或处理响应,当客户端重新联网时会尝试使用已被使用的刷新令牌。由于这可能发生在重用间隔期之外,会导致会话突然意外终止。

若重用尝试不符合上述两个例外情况,整个会话将被视为终止,所有关联的刷新令牌都会被标记为已撤销。您可以在认证设置页面的"高级设置"中禁用此行为,但通常不建议这样做。

该机制旨在防范潜在安全风险,例如当刷新令牌通过日志泄露(如记录cookie、请求体或URL参数)或存在漏洞的第三方服务器从用户处被盗时。它无法防护用户设备上的会话被盗用的情况。

使用访问令牌和刷新令牌相比传统会话有哪些优势?

传统上,用户会话是通过存储在cookie中的唯一字符串实现的,该字符串标识了用户在特定浏览器上的授权状态。应用程序会使用这个唯一字符串在每次API调用时不断获取关联的用户信息。

与基于JWT的方法相比,这种传统方式存在一些权衡:

  • 如果认证服务器或其数据库崩溃或短暂不可用(哪怕只有几秒钟),整个应用就会瘫痪。安排维护或处理临时错误变得非常困难。
  • 认证服务器的故障可能引发跨系统和API的连锁故障,导致整个应用系统瘫痪。
  • 所有需要认证的请求都必须经过认证服务器路由,这给所有请求增加了额外的延迟开销。

Supabase Auth更倾向于使用基于JWT的访问令牌和刷新令牌方案,因为会话信息被编码在短期有效的访问令牌中,使得令牌可以在API和系统间传递,而不依赖于中心服务器的可用性或性能。这种方法增强了应用对临时故障或性能问题的容忍度。此外,主动刷新访问令牌的特性使得应用即使在重大故障期间也能可靠运行。

这种方式也更有利于成本优化和扩展,因为认证系统的服务器和数据库只需处理认证相关的流量。

如何确保用户登出后访问令牌(JWT)无法使用

大多数应用程序很少需要如此严格的保证。建议将JWT过期时间调整为可接受的值。如果仍然需要此功能,应尽量仅在应用程序中最敏感的操作上使用此验证逻辑。

当用户登出时,受影响的会话会从数据库中完全删除。您可以检查JWT中的session_id声明是否对应auth.sessions表中的一行记录。如果该记录不存在,则表示用户已登出。

请注意,当会话达到最大生命周期(时间限制)或不活动超时时,系统不会主动终止这些会话。这些会话会在达到该状态后24小时内逐步清理。这样可以让您调整值或回滚更改,而不会造成意外的用户体验问题。

这种方式是可行的,但仅适用于采用传统纯服务器端Web应用架构的应用,即所有应用逻辑都在服务器端实现,仅返回渲染后的HTML。

如果您的应用使用任何客户端JavaScript来构建丰富的用户体验,那么使用HTTP-Only Cookie就不可行,因为只有您的服务器能够读取和刷新用户会话。浏览器将无法访问访问令牌和刷新令牌。

因此,Supabase JavaScript库仅提供有限支持。您可以在服务器端创建Supabase客户端时覆盖storage选项,将值存储在Cookie或您偏好的存储方式中,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
import { } from '@supabase/supabase-js'const = ('SUPABASE_URL', 'SUPABASE_ANON_KEY', { : { : { : () => { return .('FETCHED_COOKIE') }, : () => {}, : () => {}, }, },})

customStorageObject应实现Storage接口中的getItemsetItemremoveItem方法。这些方法的异步版本也受支持。

使用Cookie存储访问令牌和刷新令牌时,请确保Cookie的ExpiresMax-Age属性设置为非常遥远的未来时间戳。浏览器会清除Cookie,但会话在Supabase Auth中仍保持活跃状态。因此最好让Supabase Auth控制这些令牌的有效期,并指示浏览器始终无限期存储这些Cookie。