太好了 我们正在逐步推进。
我会逐一处理这些问题。
增量授权
你无法在首次请求时请求组权限,是因为你此时还不知道是谁在登录,也不知道他们愿意分享哪些信息。你可以通过 google_oauth2_hd 设置将 Google 关联组映射限制为使用特定托管域的用户,但这会相当大地限制该功能的适用范围。拥有一个团队,其中既使用 Google Apps,又有希望使用 Google 认证的“公开”用户,这种情况相当常见。
*编辑:我需要澄清一点,如果你请求了组范围,但用户无法授予(例如,他们的托管域尚未像上述那样将权限委托给非管理员用户),那么认证将会失败。你不能在请求必需范围的同时请求可选范围。
此外,这种方法(即在实施各种认证方法时,将请求组范围作为标准做法)可以说比替代方案更针对 Google,因为你并不总是拥有类似“托管域系统”的机制来限制登录。例如,我可能错了,但我不认为有一种方法可以将 Github OAuth2 登录限制为特定的 Github 组织。
换句话说,在许多情况下,你将面临这样的选择:要么要求所有使用该认证方法的用户授予相关的 groups 范围,要么根本不使用该功能。这种方法在某些上下文中可能有效,但在许多情况下行不通。这种增量授权方法为不同的认证方法在实施该功能时提供了更大的灵活性。
诚然,Google 一直在 OAuth2 领域倡导增量授权,例如,关于该主题的工作论文都是由 Google 员工撰写的:
然而,这个概念并非 Google 独有(其他人不一定称之为“增量授权”)。它在移动应用的不同形式中相当常见,并且正在被其他提供商在 OAuth2 中采用。例如,这是 Facebook 关于同一主题的文档 。
你可能已经熟悉这个领域了,但以下做法被认为是“最佳实践”:
告知用户你将请求比标准基本信息更多的权限;
也许还要说明原因。
如果用户在 Discourse 登录表单中点击“使用 Facebook 注册”,然后除了电子邮件外,还被要求访问其 Facebook 组,他们可能会放弃。Facebook 是这样表述的:
一般而言,应用程序请求的权限越多,用户使用 Facebook 登录该应用程序的可能性就越小。事实上,我们的研究表明,请求超过四个权限的应用程序在完成的登录数量上会出现显著下降。
这就引出了一个问题:在认证时请求额外范围是否是一个好主意?基本上,我得出的结论是我们没有其他好的选择。在某些场景下(正如你所暗示的),需要立即访问组信息;此外,现实情况是,如果不在一开始就请求,许多用户不会在个人资料或组页面等服务中采取额外的步骤来授权其组。
这必须在认证时完成,这又回到了上述问题,也是我实现“二次授权”系统的原因。它确实旨在成为一个轻量级的“系统”,因为其他服务(如 Facebook 或 Github)实施二次授权请求以在用户认证后(并可选地通过与其基本信息相关的某些测试)获取用户组访问权限相对容易。
每个提供商只需:
返回一个包含 secondary_authorization_url 的结果
使用 state 参数来检测用户处于哪个授权请求阶段
为 users/omniauth_callbacks/secondary_authorization.html.erb 提供 omniauth_secondary_authorization_description。例如,这是 Google 的示例,用户在确认二次授权重定向前会看到它:
由于您使用 %{domain} 邮箱登录,我们需要请求查看您的 %{domain} 组的权限。
这些部分都不是 Google 特有的。
我想在这里做的是允许用户对二次请求说“不”,但仍能完成认证。在 Google Apps HD 场景中,这其实不是问题,因为如果他们的账户属于托管域,他们不太可能想或能够拒绝。不过,为了涵盖各种认证场景,我认为应该对此进行适配。
最后,还需要注意的是,associated_groups 要正常工作,并不必须 进行二次授权。认证提供商可以直接在首次请求时请求组范围,然后在收到第一个响应后将组添加到认证结果中。事实上,我们应该在基本的 oauth2 和 openid connect 插件中将此作为一个选项构建进去。
提供商域名
我认为在 associated_groups 表中确实需要某种形式的次要标识符,以便站点管理员可读。存在许多场景,仅凭组名可能不足以区分。例如,每个服务中类似概念之间可能存在名称冲突:
多域名 Google 组管理(一个工作区中也可以有多个域名)
多组织 Github 组管理
多服务器 Discord 角色管理
等等
我们或许可以将 domain 改为 namespace。我们可以将其包含在组的 name 中,但这会给我们带来任何优势吗?也许在某个时候按“域名”或“命名空间”进行查询会很有用。是的,也许 namespace 比 domain 更好。
它需要是“管理员可读”的原因是,它用于管理员在组 UI 中看到的标签,部分是为了消除歧义。
我正在考虑是否尝试在此处也存储一个 provider_id(如果存在的话)。未来或许会很有用。
david:
我们需要在这里小心。任何组成员资格都必须在用户首次加载站点之前 分配。否则,他们第一次登录时将看不到特定于组的内容(例如安全分类)。因此,我认为 user_associated_group 记录的更改应同步处理。
但对于 group_associated_group 记录的更改,我认为你是对的。那里的更改可能会影响数千名用户,因此需要 分批 处理。我想我会先尝试同步处理,并在 UI 中显示加载旋转图标。这样,管理员就能清楚地看到它何时运行/完成。
如果我们发现它接近 30 秒(Unicorn 请求超时),那么可能需要考虑使用后台作业。
我们可能还需要考虑在此处添加一些 DistributedMutex 锁定。例如,如果用户在 group_associated_group 更改正在处理期间 登录,会发生什么?一旦我们最终确定整体架构,我很乐意在 GitHub 上讨论这类问题。
是的,同意所有这些,也感谢你的建议。我会尝试处理这部分,我们可以在 PR 中进一步讨论。