好吧,我有一个网站,你可以在这里注册和登录。您也可以使用您的facebook、twitter或linkedin帐户登录。
重要的是,用户只能注册一个帐户。所以不知何故,如果用户使用不同的登录方法,我想合并他们的帐户。解决这个问题的最佳解决方案是什么?
例如,用户使用他的Facebook帐户登录。我用这些数据自动为他注册了一个账户。我应该发送一封包含我们网站用户名和密码的电子邮件吗?(如果Facebook的政策允许的话)。我应该给他们第二个屏幕,让他们填写用户名和密码吗?但这并不是用你的Facebook帐户登录的想法。它应该简化你的参与程序。
用户也有可能在我们的网站上注册了自己,下次登录时使用他的推特帐户。如何将这两个帐户合并为一个?最好的方法是什么?
所以基本上我的问题是:我有四种不同的方式让用户成为我们网站的成员。如果用户决定使用多种方式,我如何确保所有这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/
我现在面临着完全相同的任务。我设计的很简单,但效果很好。
核心思想是本地站点标识和第三方站点标识的模型保持隔离,但稍后会链接。因此,每个登录到该网站的用户都有一个本地标识,该标识映射到任何数量的第三方网站标识。
本地身份记录包含最少的信息——它甚至可以是一个字段——只是一个主键。(对于我的应用程序,我不在乎用户的电子邮件、姓名或出生日期——我只想知道他们是一直登录该帐户的人。)
第三方身份包含仅与第三方验证相关的信息。对于OAuth,这通常意味着用户标识符(如id、电子邮件或用户名)和服务标识符(指示使用哪个站点或服务进行身份验证)。在应用程序的其他部分中,在数据库之外,该服务标识符与用于从该服务检索相关用户标识符的方法配对,这就是执行身份验证的方式。对于OpenID,我们采用了相同的方法,只是身份验证的方法更通用(因为我们几乎总是可以执行完全相同的协议——除了我们使用不同的身份URL,这是我们的服务标识符)。
最后,我记录了哪些第三方身份与哪些本地身份配对。要生成这些记录,流程如下:
- 用户首次使用第三方身份登录。创建一个本地身份记录,然后创建一个第三方身份记录,再将它们配对
- 在控制面板中,用户有机会通过登录第三方服务链接帐户。(这很简单。)
- 在用户无意中创建多个帐户的情况下,解决方案非常简单。当用户登录到其中一个帐户时,他会登录到另一个他以前用来登录网站的帐户(通过上面的控制面板功能)。web服务检测到此冲突(登录用户的本地标识与链接到刚登录的第三方标识的本地标识不同),并提示用户进行帐户合并
合并帐户就是合并本地身份的每个单独字段(不同应用程序会有所不同,如果您的本地身份记录中只有几个字段,则应该很容易),然后确保链接的第三方身份链接到生成的本地身份。
我倾向于发现许多网站基于电子邮件作为重叠的连接因素进行合并。
我认为这是一个可行的选择,但这取决于你对如何合并的偏好。电子邮件地址是人们用来验证网站上一些重要信息更改的主要方式,如更改密码、终止服务、帐户余额低等。它几乎就像网络的社会安全号码系统,但具有通信能力。从文化角度来说:我认为可以合理地假设,在OAuth身份验证服务中,电子邮件是一个非常独特的身份。诚然,这正是脸书和谷歌的登录表单所要求的。
我目前的思考过程。
登录页面有3个选项
- 您自己网站的会员资格
- 使用facebook登录
- 使用谷歌登录
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) 在其他时间,用户回来了,但决定点击谷歌登录
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或谷歌注册,那么不知何故,他们想使用自己网站的注册表进行注册。检测他们输入的电子邮件地址是否已经存在,询问他们密码,并在注册完成后向他们发送电子邮件确认。
我在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从第三方返回。您的数据库可以处于以下三种状态之一:
- 帐户链接到本地帐户,并且没有会话cookiepresent-->登录
- 该帐户链接到本地帐户和会话cookie存在-->合并
- 帐户未链接到本地帐户且不存在会话cookie-->注册
-
帐户未链接到本地帐户,并且会话cookie当前-->链接附加帐户
- 如何与第三方提供商进行帐户恢复
这仍然是实验领域。我还没有看到一个完美的用户体验,因为大多数服务都在第三方帐户旁边提供本地密码,因此专注于"忘记我的密码"用例,而不是其他可能出错的情况。
使用Sled,我们选择使用"需要帮助登录?",当您单击时,请询问用户的电子邮件或用户名。我们查找它,如果我们找到了一个匹配的帐户,就会通过电子邮件向该用户发送一个链接,该链接可以自动将他们登录到该服务中(一次性有效)。一旦进入,我们会将他们直接带到帐户链接页面,告诉他们应该查看并可能链接其他帐户,并向他们显示他们已经链接的第三方帐户。
这两种自动合并帐户的方法都留下了一个相当大的漏洞,允许某人接管帐户。当他们向注册用户提供合并选项时,他们似乎都假设用户就是他们所说的用户。
我建议在执行合并以验证用户身份之前,请求用户向其中一个已知的身份提供程序进行身份验证,以缓解该漏洞。
示例:用户A使用Facebook身份注册。稍后,他们会返回您的网站,尝试使用Windows Live ID进行访问并开始注册过程。您的网站将提示用户A。。。看起来你以前已经在Facebook上注册过了。请使用Facebook登录(提供链接),我们可以将您的Windows Live ID与您现有的个人资料合并。
另一种选择是在初始注册时存储共享机密(密码/个人问题),用户在合并身份时必须提供该机密,但这会让您重新开始存储共享机密。这也意味着你必须处理用户不记得共享秘密和随之而来的工作流程的情况
大多数帖子都很旧,我想谷歌的免费Firebase身份验证服务还没有出现。在使用OAuth进行验证后,您将OAuth令牌传递给它,并获得一个唯一的用户id,您可以存储该id以供参考。支持的提供商有谷歌、脸书、推特、GitHub,还可以选择注册自定义和匿名提供商。
很好的答案和上面的资源。我的贡献总结如下。。。https://github.com/JavascriptMick/learntree.org/blob/master/design/Schema.md
TLDR:分离帐户和个人模式。帐户、电子邮件和OAuth的两种变体。
账户-验证->个人
您应该允许从一个帐户登录,然后在登录时可以选择添加不同的其他帐户与之合并。