You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多租户应用集成Google Calendar API:按子域分离OAuth2令牌问题

这问题我之前做类似Slack的多租户SaaS项目时刚好碰到过,核心是要把用户身份+租户上下文牢牢绑定到OAuth令牌的全流程里,才能实现不同子域租户的权限隔离。下面是我实践下来可行的完整方案:

1. 授权请求阶段注入租户标识

Google OAuth的state参数是专门用来传递自定义上下文的,这是我们绑定租户的关键:

  • 当用户在foo.domain.com发起Google Calendar授权时,构造的授权URL里要带上state参数,格式可以是{租户标识}|{随机CSRF值},比如state=foo|abc123xyz。这里的foo就是对应子域的租户唯一ID,随机CSRF值是为了防止跨站请求伪造,必须加。
  • 等Google授权完成回调到你的应用时,你就能从state参数里解析出当前的租户ID,明确这个授权流程是属于哪个子域的。

2. 按「用户ID+租户ID」维度存储令牌

绝对不能只按用户ID存储令牌——因为同一个用户属于多个租户,每个租户的令牌必须完全独立:

  • 你的数据库令牌表要包含这些核心字段:user_id(用户唯一标识)、tenant_id(对应子域的租户ID)、access_token、refresh_token、expires_at(令牌过期时间)。
  • 比如用户John在foo.domain.com和bar.domain.com都授权后,数据库里会有两条独立的记录:一条是user_id=john_123&tenant_id=foo,另一条是user_id=john_123&tenant_id=bar,各自对应不同的令牌对。

3. API调用阶段按租户上下文取对应令牌

当用户在某个子域操作时,必须先锁定当前租户的上下文,再获取对应的令牌:

  • 第一步:从当前请求的子域(比如foo.domain.com)解析出tenant_id=foo,这一步要做严格校验,防止用户通过参数篡改租户ID(比如不能让用户手动传入tenant_id,必须从子域自动解析)。
  • 第二步:结合当前登录用户的user_id,去令牌表中查询匹配user_id和tenant_id的记录。
  • 第三步:如果查询到的access token已经过期,就用该记录对应的refresh token去Google刷新令牌,然后更新该租户下的令牌记录——绝对不能用A租户的refresh token去刷新B租户的令牌,因为它们是完全独立的。

4. 额外的权限隔离保障

为了确保万无一失,还可以做这些补充:

  • 避免跨租户令牌泄露:在应用的权限控制层,要确保任何接口都不能返回其他租户的令牌信息,哪怕是同一个用户的也不行。比如John在foo.domain.com操作时,应用只能返回他在foo租户下的令牌,不能返回bar租户的。
  • 可选:按租户配置OAuth权限范围:如果不同租户需要的Google Calendar权限不同(比如有的只需要读,有的需要写),可以在授权请求时,针对不同租户传入不同的scope参数,同时在令牌表里也存储对应的scope字段,后续API调用时校验权限范围。
  • 令牌失效处理:如果某个租户的用户取消了对应用的授权,要及时删除该租户下的令牌记录,避免后续无效调用。

5. 用户跨租户授权的体验优化

为了提升用户体验,还可以做这些细节:

  • 当用户在新租户操作时,如果已经用同一个Google账号授权过你的应用,Google会直接跳过授权确认页面,但你还是要为这个新租户创建独立的令牌记录。
  • 在用户的个人设置页面,显示他们已授权的所有租户,允许用户单独取消某个租户的授权(删除对应令牌记录)。

内容的提问来源于stack exchange,提问作者Greg Olsen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:32:26