将多个用户帐户合并在一起的架构

Posted

技术标签:

【中文标题】将多个用户帐户合并在一起的架构【英文标题】:Architecture for merging multiple user accounts together 【发布时间】:2011-10-03 17:19:30 【问题描述】:

好的,我有一个网站,您可以在其中注册并登录。您也可以使用您的 facebook、twitter 或linkedin 帐户登录。

用户只能注册一个帐户,这一点很重要。所以不知何故,如果用户使用不同的方法登录,我想合并用户的帐户。解决这个问题的最佳解决方案是什么?

例如,用户使用他的 Facebook 帐户登录。我使用这些数据自动为他注册了一个帐户。我应该发送一封包含我们网站用户名和密码的电子邮件吗? (如果这符合 Facebook 的政策)。我应该给他们第二个屏幕,让他们可以填写用户名和密码吗?但这不是使用您的 Facebook 帐户登录背后的想法。它应该简化您的参与程序。

也有可能用户已经在我们的网站上注册了自己,并且下次他使用他的 Twitter 帐户登录。如何将这两个帐户合并为一个?最好的方法是什么?

所以基本上我的问题是:我有 4 种不同的方式让用户成为我们网站的成员。如果用户决定使用多种方式,我如何确保所有这 4 种方式只创建一个帐户?确保不会给用户带来麻烦的最佳流程是什么?


编辑:

在我提出这个问题 3 年后,我自己在一系列文章中给出了答案: https://www.peternijssen.nl/social-network-authentication-setup/https://www.peternijssen.nl/social-network-authentication-google/https://www.peternijssen.nl/social-network-authentication-merging-accounts/https://www.peternijssen.nl/social-network-authentication-twitter-facebook/

【问题讨论】:

当然,最好首先通过允许 Stack Overflow 等多种登录方式来防止用户拥有多个帐户,但即便如此,版主也可以合并帐户。我为任何可以描述允许这样做的架构的人提供赏金。必须有比“更新帖子集 UserID = 2 where UserID = 1”更好的解决方案 邮箱和手机可以作为合并键,还有其他的吗? 您好,您提供的链接似乎无效。它只进入网站主页 更新了链接。不过文章已有 6 年历史了。 【参考方案1】:

我现在面临着完全相同的任务。我设计的设计比较简单,但效果很好。

核心思想是本地站点身份和第三方站点身份的模型保持隔离,但后来被链接。因此,每个登录网站的用户都有一个本地身份,该身份映射到任意数量的第三方网站身份。

本地身份记录包含最少的信息 - 它甚至可以是单个字段 - 只是一个主键。 (对于我的应用程序,我不关心用户的电子邮件、姓名或出生日期 - 我只想知道他们是一直登录此帐户的人。)

第三方身份包含仅与第三方身份验证相关的信息。对于 OAuth,这通常意味着一个用户标识符(如 id、电子邮件或用户名)和一个服务标识符(指示对哪个站点或服务进行身份验证)。在数据库之外的应用程序的其他部分,该服务标识符与用于从该服务检索相关用户标识符的方法配对,这就是执行身份验证的方式。对于 OpenID,我们采用相同的方法,只是身份验证的方法更加通用(因为我们几乎总是可以执行完全相同的协议——除了我们使用不同的身份 URL,这就是我们的服务标识符)。

最后,我会记录哪些第三方身份与哪些本地身份配对。要生成这些记录,流程如下所示:

用户首次使用第三方身份登录。创建本地身份记录,然后创建第三方身份记录,然后将它们配对。 在控制面板中,用户可以通过登录第三方服务来链接帐户。 (这很简单。) 在用户无意中创建多个帐户的情况下,解决方案非常简单。当用户登录其中一个帐户时,他会登录另一个他以前用来登录网站的帐户(通过上面的控制面板功能)。 Web 服务检测到这种冲突(登录用户的本地身份与链接到刚刚登录的第三方身份的本地身份不同)并提示用户合并帐户。

合并帐户是合并本地身份的每个单独字段的问题(这会因应用程序而异,如果您的本地身份记录中只有几个字段,应该很容易),然后确保链接的第三个-party 身份与生成的本地身份相关联。

【讨论】:

这是一个非常好的解决方案(我喜欢自动检测本地身份冲突的想法)!我想知道您是否找到了一种将用户自动登录到第一次登录应用程序后链接的“额外”帐户的方法。用户每次访问时是否必须分别登录每个帐户(如果还没有与此提供者的活动会话)? @Alexandra “额外帐户”是什么意思?您是否想问当用户通过多个不同的身份验证器登录应用程序并为每个身份验证器创建一个新的本地身份时会发生什么? 我想这需要适当的澄清和它自己的问题:***.com/questions/11060368/… 问题,但是如果用户使用相同的电子邮件地址注册到站点怎么办?我们是提示他们电子邮件已经存在(来自第三方模型的电子邮件)还是我们仍然将他们注册到网站然后我们合并了这些帐户? @user962206 出于隐私原因,许多服务都不会为您提供“真实”的电子邮件地址。例如,Twitter 不会给你任何电子邮件地址,据我所知 Facebook 会给出“user.name@facebook.com”,我怀疑有人会用它来注册。【参考方案2】:

我倾向于发现很多基于电子邮件作为重叠加入因素的网站合并。

我认为这是一个可行的选择,但同样取决于您对如何合并的偏好。电子邮件地址是人们用来验证您网站上某些重要信息更改的主要方式,例如更改密码、终止服务、帐户余额不足等......它几乎就像网络的社会安全号码系统,但具有通信能力.文化上:我认为可以合理地假设电子邮件是跨 OAuth 身份验证服务的一个非常独特的身份。当然,这是 Facebook 和 Google 的登录表单所要求的。

我目前的思考过程。

登录页面有3个选项

您自己网站的会员资格 用脸书登录 用谷歌登录

1) 用户首次登录:触发注册流程,首次创建和填充帐户。

 if the user logins using Facebook (or whatever 3rd party login)
      1) call the Facebook api asking for their information (email, name, etc...) 
      2) create an account membership entry in your database somewhat like this 

         Table = Users
         [ UserId   |       Email             | Password ]
         [    23     | "newuser@coolmail.com" |  *null*  ]

      3) create an external auths entry like so
         *ProviderUserId is the unique id of that user on the provider's site

         Table = ExternalAuths
         [ ExternalAuthId  |  User_UserId   | ProviderName |   ProviderUserId  ]
         [    56           |      23        |   Facebook   |  "max.alexander.9"]

 if the user wants to create an account with your own registration it would just be this           

         Table = Users
         [ UserId   |       Email           |   Password  ]
         [    23     | newuser@coolmail.com |  myCoolPwd  ]

2) 在其他某个时间,用户回来但决定点击 Google 登录

      1) call the Google api asking for their information (email, name, etc...) 

      2) once you get the email, match it up to the userId entry with the existing email 

      3) create an additional External auth entry as such

         Table = ExternalAuths
         [ ExternalAuthId  |  User_UserId   | ProviderName |   ProviderUserId  ]
         [    56           |      23        |   Facebook   |  "max.alexander.9"]
         [    57           |      23        |    Google    |  "1234854368"     ]

3) 现在您已经合并了您信任的帐户,您的数据库条目中的电子邮件与您信任的来自外部登录的帐户相同。

所以对于后续登录

那么,如果您先有外部登录,然后希望用户稍后能够使用密码登录,该怎么办?

我看到了两种简单的方法来做到这一点

在从外部身份验证创建帐户时首次登录时,要求他们输入密码以完成他们首次进入您的应用程序

如果他们已经先使用 facebook 或 google 注册,那么不知何故想使用您自己网站的注册表单进行注册。检测他们输入的电子邮件地址是否已经存在,要求他们输入密码,并在注册完成后向他们发送电子邮件确认。

【讨论】:

当您使用外部身份验证方案(Facebook、Google、Twitter 等)时,您将永远无法访问外部提供商密码。上面示例中的密码是您的 OWN Web 应用程序的密码。 Twitter 不向用户提供电子邮件。如果 Twitter UserId 不存在,那么在他们的第一次身份验证之后,提示他输入电子邮件和密码。如果电子邮件和密码存在,则链接帐户。如果没有,请存储电子邮件和密码以进行无缝后续身份验证。这有帮助吗? 附带说明一下,现在可以通过request permissions 为您的 Twitter 应用程序获取用户的电子邮件地址。 Facebook 是否让人们验证他们的电子邮件?例如,我知道 Github 没有。恶意用户可以使用其他人的电子邮件名称注册 Github,然后使用此策略访问他们在您网站上的帐户。 (Github 的 API 会告诉您他们的电子邮件是否经过验证 - 您可以拒绝任何使用未经验证的电子邮件地址通过 Github 进行身份验证的人) 如果用户通过社交媒体注册,在社交媒体上编辑他们的电子邮件地址,其他人使用相同的电子邮件地址,在社交媒体上注册,然后登录,他们将被与他人帐户合并。这不太可能,但仍然是违规行为。还是我错过了什么。【参考方案3】:

我已经通过 sled.com 解决了这个问题。关于创建帐户和支持多个第三方帐户登录,这里存在多个问题。其中一些是:

是否需要同时支持本地密码和第三方登录?

对于 sled.com,我决定放弃本地密码,因为它增加的价值很小,而且保护密码输入表单的额外成本。有许多已知的破解密码的攻击,如果你要引入密码,你必须确保它们不容易破解。您还需要将它们存储在单向哈希或类似的东西中,以防止它们被泄露。

您希望在支持多个第三方帐户方面具有多大的灵活性?

听起来您已经选择了三个登录提供程序:Facebook、Twitter 和 LinkedIn。这很好,因为这意味着您正在使用 OAuth 并与一组定义明确的可信提供者合作。我不喜欢 OpenID。剩下的问题是您是否需要支持来自同一提供商的多个第三方帐户(例如,一个本地帐户与两个 Twitter 帐户相关联)。我假设没有,但如果你这样做,你需要在你的数据模型中适应它。

对于 Sled,我们支持使用 Facebook、Twitter 和 Yahoo! 登录并在每个用户帐户中为每个帐户存储一个密钥:“_id”:“djdjd99dj”,“yahoo”:“dj39djdj”,twitter:“3723828732”,“facebook”:“12837287”。我们设置了一系列约束来确保每个第三方帐户只能链接到一个本地帐户。

如果您要允许来自同一个第三方提供商的多个帐户,您将需要使用列表或其他结构来支持这一点,并使用所有其他限制来确保唯一性。

如何关联多个帐号?

用户第一次注册您的服务时,他们首先会去第三方提供商处,然后返回经过验证的第三方 ID。然后,您为他们创建一个本地帐户并收集您想要的任何其他信息。我们收集他们的电子邮件地址并要求他们选择一个本地用户名(我们尝试使用来自其他提供商的现有用户名预先填充表单)。拥有某种形式的本地标识符(电子邮件、用户名)对于以后的帐户恢复非常重要。

如果浏览器没有现有帐户的会话 cookie(有效或过期),并且未找到使用的第三方帐户,则服务器知道这是第一次登录。我们会尝试通知用户,他们不仅仅是在登录,而是在创建一个新帐户,这样如果他们已经有一个帐户,他们希望可以暂停并改用现有帐户登录。

我们使用完全相同的流程来链接其他帐户,但是当用户从第三方返回时,有效会话 cookie 的存在用于区分尝试将新帐户链接到登录操作。我们只允许每种类型的一个第三方帐户,如果已经有一个链接,则阻止该操作。这应该不是问题,因为如果您已经有一个(每个提供商),则链接新帐户的界面将被禁用,但以防万一。

如何合并帐号?

如果用户尝试关联已关联到本地帐户的新第三方帐户,您只需提示他们确认是否要合并这两个帐户(假设您可以使用您的数据集处理此类合并 -说起来容易做起来难)。您还可以为他们提供一个特殊按钮来请求合并,但实际上,他们所做的只是关联另一个帐户。

这是一个非常简单的状态机。用户从第三方返回,并带有第三方帐户 ID。您的数据库可以处于以下三种状态之一:

    该帐户已链接到本地​​帐户,并且没有会话 cookie 现在 --> 登录 该帐户已链接到本地​​帐户和 会话 cookie 存在 --> 合并 该帐户未链接到 本地帐户且不存在会话 cookie --> 注册

    帐户未链接到本地​​帐户,并且会话 cookie 是 present --> 关联其他帐号

    如何通过第三方提供商执行帐户恢复?

这仍是试验领域。我还没有看到完美的用户体验,因为大多数服务都在第三方帐户旁边提供本地密码,因此专注于“忘记我的密码”用例,而不是其他所有可能出错的情况。

对于 Sled,我们选择使用“需要帮助登录?”当您单击时,向用户询问他们的电子邮件或用户名。我们查找它,如果我们找到匹配的帐户,则通过电子邮件向该用户发送一个链接,该链接可以自动将他们登录到服务中(一次性使用)。进入后,我们会将他们直接带到帐户关联页面,告诉他们应该查看并可能关联其他帐户,并向他们显示他们已经关联的第三方帐户。

【讨论】:

这个解决方案对我来说更好,谢谢! 会话 cookie 不足以识别用户。如果其他用户使用相同的设备怎么办?【参考方案4】:

这两种自动合并帐户的方法都会留下一个相当大的漏洞,允许某人接管帐户。当他们向注册用户提供合并选项时,他们似乎都假设用户就是他们所说的那个人。

我对缓解漏洞的建议是在执行合并之前请求用户向已知身份提供者之一进行身份验证,以验证用户的身份。

示例:用户 A 使用 Facebook 身份注册。稍后,他们会返回您的站点并尝试使用 Windows Live ID 访问并开始注册过程。您的网站将提示用户 A... 看起来您之前已在 Facebook 注册过。请使用 Facebook 登录(提供链接),我们可以将您的 Windows Live ID 与您现有的个人资料合并。

另一种选择是在初始注册时存储用户在合并身份时必须提供的共享机密(密码/个人问题),但这会让您重新开始存储共享机密的业务。这也意味着您必须处理用户不记得共享密钥以及随之而来的工作流程的情况。

【讨论】:

如果您没有收到提供商的电子邮件地址,您会怎么做?【参考方案5】:

大部分帖子都已经很老了,我猜 Google 的免费 Firebase Authentication 服务还没有出现。使用 OAuth 验证后,您将 OAuth 令牌传递给它并获得一个唯一的用户 ID,您可以将其存储以供参考。支持的提供者包括 Google、Facebook、Twitter、GitHub,并且可以选择注册自定义和匿名提供者。

【讨论】:

有些用例认为 firebase 没有意义。例如具有多个登录的 Intranet 系统..【参考方案6】:

以上很好的答案和资源。我的贡献总结在这里...https://github.com/javascriptMick/learntree.org/blob/master/design/Schema.md

TLDR:单独的帐户和人员架构。帐户、电子邮件和 OAuth 的 2 种变体。

账户 -authenticates-> 个人

【讨论】:

【参考方案7】:

您应该允许从 一个 帐户登录,然后在登录时选择添加不同的其他帐户以与其合并。

【讨论】:

如果用户不这样做,并且发现自己拥有 4 个不同的帐户,会发生什么?在这种情况下,您如何创建允许合并的架构? 根据您的需要,这实际上可能是一个有效的建议。我只是想警告那些认为合并或链接帐户可以在以后进行的人:如果您认为这是您以后可能想要的东西,那么您可能希望在您的数据库设计开始时为此做好准备:一个选项是有一个用户组和一个用户映射。您可以将 OAuth 用户 ID 或电子邮件和密码用户 ID 映射到用户组。

以上是关于将多个用户帐户合并在一起的架构的主要内容,如果未能解决你的问题,请参考以下文章

合并多个 github 用户帐户并转移提交

django-allauth:将多个社交帐户链接到单个用户

如何编写查询以将两个帐户及其活动日志合并为一个? [关闭]

如何在 MongoDb 中为这样的链接帐户建模?

用于登录的用户名或电子邮件 ID?

Flutter 使用具有作用域模型的多个模型