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

好吧,我有一个网站,您可以注册自己并login。 您也可以使用您的Facebook,Twitter或LinkedIn帐户login。

用户只需注册一个帐户就很重要。 所以不知何故,我想合并用户的帐户,如果他们使用不同的方法login。 什么是解决这个问题的最佳解决scheme?

例如,用户使用他的Facebook帐户login。 我使用这些数据自动为他注册一个帐户。 我应该使用我们网站的用户名和密码发送电子邮件吗? (如果Facebook的政策没有问题)。 我应该给他们第二个屏幕,他们可以填写用户名和密码? 但是,这不是用你的Facebook账号login的想法。 它应该简化您的程序参与。

用户也可以在我们的网站上注册自己,并在下一次用他的Twitter帐户login。 我怎样才能将这两个账户合并为一个? 什么是最好的方法?

所以基本上我的问题是:我有4种不同的方式,用户成为我们网站的成员。 如何确保用户决定使用多种方式时,所有这四种方式只能创build一个帐户? 什么是最好的stream程,以确保它不会成为用户自己的麻烦?


编辑:

在我问这个问题3年之后,我在一系列文章中自己给出答案: http : //www.sitepoint.com/series/using-social-networks-as-a-login-system/

我目前面临完全相同的任务。 我所devise的devise相当简单,但效果很好。

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

本地身份logging包含最less的信息 – 甚至可以是一个字段 – 只是一个主键。 (对于我的申请,我不关心用户的电子邮件,姓名或出生date – 我只是想知道他们是一直login到这个帐户的人。)

第三方身份包含仅与第三方身份validation有关的信息。 对于OAuth来说,这通常意味着用户标识符(如ID,电子邮件或用户名)和服务标识符(表明通过身份validation的站点或服务)。 在应用程序的其他部分,在数据库之外,该服务标识符与用于从该服务检索相关用户标识符的方法配对,并且是如何执行authentication。 对于OpenID,我们使用相同的方法,除了authentication的方法更普遍(因为我们几乎总是可以执行完全相同的协议 – 除了我们使用不同的身份URL,这是我们的服务标识符)。

最后,我保留了哪些第三方身份与本地身份配对的logging。 要生成这些logging,stream程如下所示:

  • 用户第一次使用第三方身份login。 创build本地身份logging,然后创build第三方身份logging,然后进行配对。
  • 在控制面板中,用户可以通过login到第三方服务来连接帐户。 (很简单,这是如何工作的。)
  • 在用户无意中创build多个账户的情况下,解决scheme非常简单。 用户login其中一个帐户时,他login到以前用于login该站点的另一个帐户(通过上面的控制面板function)。 Web服务检测到此冲突(即login用户的本地身份与链接到刚刚login的第三方身份的本地身份不同),并提示用户进行帐户合并。

合并帐户是合并本地身份的每个单独字段的问题(如果您在本地身份logging中只有一些字段,则该字段将根据应用程序的不同而不同),然后确保链接的第三方身份与所得到的本地身份相关联。

我已经通过这个与sled.com。 关于创build帐户和支持多个第三方帐户login有多个问题。 他们之中有一些是:

  • 你需要同时支持本地密码和第三方login吗?

对于sled.com,我决定放弃本地密码,因为它增加了一个小的值,并且在保护密码input表格方面花费了额外的费用。 有许多已知的破解密码的攻击,如果你要引入密码,你必须确保它们不容易破解。 您还需要将其存储在单向哈希或类似的东西中,以防止它们泄漏。

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

听起来你已经select了三个login提供商:Facebook,Twitter和LinkedIn。 这很好,因为这意味着您正在使用OAuth并与一组定义明确的可信提供者一起工作。 我不是OpenID的粉丝。 剩下的问题是,如果你需要支持来自同一供应商的多个第三方账户(例如一个本地账户有两个Twitter账户链接)。 我假设没有,但如果你这样做,你将需要适应你的数据模型。

对于雪橇,我们支持loginFacebook,Twitter和Yahoo! 并在每个用户帐户内存储每个密钥:{“_id”:“djdjd99dj”,“yahoo”:“dj39djdj”,twitter:“3723828732”,“facebook”:“12837287”}。 我们设置了一系列限制,以确保每个第三方帐户只能链接到一个本地帐户。

如果您打算允许来自同一个第三方供应商的多个账户,您将需要使用列表或其他结构来支持该账户,以及所有其他限制以确保唯一性。

  • 如何链接多个帐户?

用户第一次注册您的服务,他们首先去第三方提供商,并返回一个validation的第三方ID。 然后,您为他们创build一个本地帐户,并收集您想要的任何其他信息。 我们收集他们的电子邮件地址,并要求他们select一个本地用户名(我们试图用他们现有的用户名从其他提供商预先填写表格)。 具有某种forms的本地标识符(电子邮件,用户名)对于稍后的帐户恢复非常重要。

服务器知道这是第一次login,如果浏览器没有一个现有帐户的会话cookie(有效或过期),并且没有find使用的第三方帐户。 我们尝试通知用户,他们不仅仅是login,而是创build一个新帐户,以便如果他们已经有一个帐户,他们会希望暂停和login他们的现有帐户。

我们使用完全相同的stream程链接其他帐户,但是当用户从第三方回来时,会使用有效的会话cookie来区分尝试将新帐户与login操作关联起来。 我们只允许每个types的一个第三方帐户,如果已经有一个链接,阻止行动。 这不应该是一个问题,因为如果你已经有一个(每个提供商),但是为了以防万一,链接一个新帐户的界面被禁用。

  • 如何合并帐户?

如果用户尝试链接已经链接到本地​​帐户的新的第三方帐户,您只需提示他们确认他们想要合并这两个帐户(假设您可以处理与您的数据集的这种合并 – 往往更容易说比做)。 您也可以为他们提供一个特殊的button来请求合并,但实际上他们所做的只是连接另一个帐户。

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

  1. 该帐户链接到本地​​帐户,不存在会话cookie – >login
  2. 该帐户链接到本地​​帐户,会话cookie存在 – >合并
  3. 该帐户没有链接到本地​​帐户,并且没有会话cookie存在 – >注册
  4. 该帐户没有链接到本地​​帐户,会话cookie存在 – >链接附加帐户

    • 如何使用第三方提供商执行帐户恢复?

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

借助Sled,我们select使用“需要帮助login?” 当你点击时,询问用户的电子邮件或用户名。 我们查看它,如果我们find一个匹配的帐户,发送电子邮件给用户一个链接,可以自动login到服务(一次性好)。 一旦进入,我们直接将它们带到账户链接页面,告诉他们应该看看并且可能链接更多账户,并向他们展示他们已经链接的第三方账户。

我倾向于发现许多基于电子邮件合并的网站是重叠的联合因素。

我可以看到这是一个可行的select,但这又取决于你如何合并的偏好。 电子邮件地址是人们用来validation网站上重要信息变化的主要方式,例如更改密码,终止服务,账户余额不足等等。这几乎就像networking的社会安全号码系统,但具有沟通的能力。 在文化上:我认为有理由认为电子邮件是跨OAuth身份validation服务的一个非常独特的身份。 当然,这是Facebook和Google所要求的loginforms。

我目前的思维过程。

login页面有3个选项

  • 您自己的网站的成员资格
  • 用facebooklogin
  • login谷歌

1)用户第一次login:触发注册stream程,首次创build并填充账户。

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)在其他时间,用户回来,但决定点击谷歌login

  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)现在,您已经合并了您信任的数据库条目上的电子邮件与您从外部login信任的电子邮件相同的帐户。

所以对于后续login

那么,如果您先login外部login,然后又想让用户稍后能够使用密码login呢?

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

  • 在通过外部authentication创build帐户时的任何首次login时,要求他们input密码以完成首次login到您的应用程序

  • 如果他们已经注册使用Facebook或谷歌,然后想用自己的网站的registry格注册。 检测他们input的电子邮件地址是否已存在,询问他们的密码,并在注册完成后向他们发送电子邮件确认。

这两种自动合并帐户的方法都有一个很大的漏洞,允许有人接pipe一个帐户。 他们似乎都假定用户是他们所说的,当他们向注册用户提供合并选项时。

我对缓解该漏洞的build议是在执行合并之前请求用户使用其中一个已知身份提供程序进行身份validation,以validation用户的身份。

例如:用户A注册Facebook身份。 过了一段时间,他们会回到您的网站,尝试访问Windows Live ID并开始注册过程。 您的网站将提示用户A …看起来您以前已经在Facebook上注册过。 请loginFacebook(提供链接),我们可以合并您的Windows Live ID与您现有的configuration文件。

另一种方法是在初始注册时存储共享密码(密码/私人问题),用户在合并身份时必须提供此密码,但是这会让您重新进入存储共享密码的业务。 这也意味着你必须处理用户不记得共享密钥和与之相关的工作stream的场景。

大部分post都很旧,我想Google的免费的Firebase身份validation服务还没有完成。 在使用OAuth进行validation之后,您将OAuth令牌传递给它,并获取可以存储以供参考的唯一用户标识。 支持的提供商是Google,Facebook,Twitter,GitHub,并有一个选项来注册自定义和匿名提供商。

您应该允许从一个帐户login,然后login时,可以select添加不同的其他帐户与其合并。