OAuth2 社交登录 - Facebook, Google, Twitter, PayPal - ASP.NET MVC C# 开源库
ASP.NET MVC C# 开源库,用于抽象化 Facebook, Google, Twitter, PayPal 等 OAuth 提供商的社交登录。
距离我上次在这里写文章已经有一段时间了。我想,忙于编写各种网站影响了我写得更少。然而,当我回想时,我认为不怎么阅读对我的影响更大。写作与阅读有很多关系。当你阅读别人的作品时,它会激发你添加自己的想法。
在我看来,我终于读完了我巨大的待读书单中的两本书。
别让我思考: 网页可用性常识指南 | 告别文字: 写作有效的网页内容 |
我不能说这两本书是严格关于编程的。但这实际上是件好事。随着时间的推移,我们这些开发人们与之互动技术的人,越来越需要关注“为什么”而不是“如何”。编程语言和工具变得越来越好——给了我们更多时间来专注于让我们的应用程序和网站更加用户友好。
就我而言,上面提到的两本书让我对维护的网站上的登录产生了深刻的思考。当然,在考虑社交登录时,会想到一些缺点(隐私是最大的)。但是,有一点是我永远无法反驳的,那就是社交登录如何让最终用户的账户创建和登录变得如此容易。
另一方面,对我们开发者来说,社交登录可能非常复杂。当然,集成 Facebook 社交登录可能比你自己实现完整的电子邮件注册要容易。表单验证、发送电子邮件、电子邮件中的验证令牌——你懂的。但是,当你需要下载并集成 Facebook SDK,然后是 Twitter SDK、PayPal SDK……并使所有这些工作起来,同时与大量不熟悉且不必要的类作斗争时……这并不容易。
在接下来的文章中,我将阐述我为这个问题提出的解决方案。但是,一如我所有文章的惯例,在进入正文之前,这里是目录,帮助你导航文章。
目录
- 现有的 ASP.NET C# OAuth 社交登录解决方案
- 本文介绍的 OAuth2Login 库
- 如何设置 OAuth2Login 库
- 解释演示和代码结构
- 关于库的一些说明
- 就这么多了?!
- 未来计划
如果你想跳过关于我不喜欢的现有解决方案的讨论,直接阅读我介绍的 OAuth2Login 库,请随意跳转到“本文介绍的 OAuth2Login 库”章节。
如果你已经设置了现有应用,只想了解如何将其与本文介绍的库一起使用,请浏览“Web.Oauth.config 文件”,然后继续阅读“解释演示和代码结构”章节。
现有的 ASP.NET C# OAuth 社交登录解决方案
在深入我的解决方案之前,值得看看现有的方案。也许你会找到更适合你用例的。只要这篇文章能帮助你解决问题,我就会感到满足。
原生 OAuth2 社交登录库(SDK)
显然,所有提供社交登录的公司都会提供自己的库/SDK。我只能想象他们投入了多少人力和数百万美元来开发和维护它们。讽刺的是,这也是原生库的一大问题。在服务器端,公司使用他们最擅长的技术开发和维护 OAuth2 接口。但现在,管理层会说——好了,现在我们需要为我们的 API 开发包装库。我搜索了最流行的编程语言,似乎我们需要:PHP、Ruby、C#、JavaScript、Python、Java SDK。至少这么多。赶快做出来。
我将跳过关于雇佣人们学习新编程语言,然后立即交给他们创建供其他开发者使用的库的任务时会发生什么。更大的问题是,你现在有许多包装库需要维护和更新。考虑到这一点,我认为目前的情况已经是最好的了。当然,如果你去 Facebook API 错误跟踪器,你可能会笑出声来,因为 Facebook 正在推广使用的 SDK 中有多少个已知错误。但考虑到他们在 Graph API 之上维护 5 个 SDK(iOS、Android、JavaScript、PHP 和 Unity SDK),错误的数量是可以理解的。
首先,让我们列出你想要访问的提供社交登录的公司开发者页面。
-
Facebook - https://developers.facebook.com - 毫无疑问,当你提到社交登录时,第一个想到的公司就是 Facebook。我喜欢 Facebook 如何处理整个 SDK/登录/点赞的场景。他们允许其他公司更容易地获取 Facebook 登录用户,作为回报,Facebook 可以追踪人们在其他集成其 widget 的网站上的活动,更好地进行用户画像,然后通过广告将这些数据出售给广告商。对他们来说,这很棒。
-
Twitter - https://dev.twitter.com/ - 我一直认为 Twitter 是 Facebook 年轻、更傲慢的弟弟(是的,我知道声称有公司比 Facebook 更傲慢这个笑话)。你可以在他们支持论坛上关于“OAuth2,四年磨一剑”和“请在用户登录时提供电子邮件”的帖子中看到这一点。只需关注我给你的两个链接,然后尽情阅读。
-
PayPal - https://developer.paypal.com/ - 我在 PayPal SDK 和集成方面有相当长的经验(正如你从我的另一篇文章——面向 C# - ASP.NET 开发者的 PayPal 入门——中看到的)。我不能说他们一直是公司如何支持依赖其 API 的开发者的闪耀榜样。如果你曾经被转到他们的技术支持——祝你好运,找到你问题的解决方案。但是,我喜欢他们最近在 SDK 方面的举动,并希望情况会变得更好。
-
Google - https://developers.google.com/ - 根据我的经验,Google 的 API 可能是最稳定、最不容易让你失望的。这很好,因为你永远不可能在你联系 Google Developer 支持时找到一个了解情况的开发人员。
我非常喜欢的一点是,在过去几年里,上述公司都对其 SDK 进行了开源。说到这个,开源应该是默认的选择。任何免费使用的 SDK 都应该开源。这样,在出现 bug 时,你就不必给我写充满抱怨的电子邮件给你的支持部门,而是可以自己修复它。以下是 Facebook 和 PayPal SDK 的链接。如果你知道 Twitter 和 Google 在哪里发布了他们的 SDK 源代码,请留下评论,我将把你的条目添加到列表中。
Microsoft 的 OAuth2 和社交登录库
自从 ASP.NET / C# 诞生 15 年以来,Microsoft 一直试图推出一些库,让我们可以轻松地在 Visual Studio 中处理“当前热门任务”。现在,我很想知道他们是如何选择和激励那些负责实现相关库/插件的人的。
我之所以这么说,是因为我尝试使用过的大多数 Microsoft 开发的库(.NET 平台核心之外的)都相当糟糕。好像它们在发布之前从未在实际项目上进行过测试。所以当你尝试使用它们时,你会发现 tons 的先决条件、愚蠢的约定和配置上的怪癖。
幸运的是,我可以说是 OAuth2 在某种程度上是个例外。当然,你将被迫切换到 OWIN 和 Katana——但至少提供的指南和示例都很好。你可以使用这篇文章作为起点。
我还很惊喜地发现了OAuthforaspnet.com 网站。我喜欢 Microsoft 从闭源库转向“让我们支持那些开源了我们想要的东西的人”的事实。现在,如果他们能让自己的员工也为这样的仓库做出贡献,那就更好了。希望我们能看到Entity Framework 的开发模式应用在这种情况下。
本文介绍的 OAuth2Login 库
看看现有的选项,很容易问:既然有可用的选项,为什么还要自己编写库?四个简单的原因:
- 向后兼容性——Microsoft 目前提供的都是面向 OWIN 和 Katana 的。当然,你可以让它与你已有的网站一起工作,但当你只需要一个简单的网站登录时,为什么要强迫自己使用全新的技术呢?
- 独立性——Microsoft OAuth 提供商的上一代版本会在你的数据库中创建预定义的表。我不知道你怎么样,但我不喜欢某个库为了运行而污染我的数据库。
- 可配置性——我一直感到困惑的一点是,为什么当前库的配置大多都写在代码里。也许这与“易用性”有关。库的开发者不想解释人们在哪里以及如何设置配置值,所以他们直接将配置留在了代码中(取消注释这些行,仅此而已)。虽然这肯定更容易,但这是一种糟糕的做法。配置需要放在配置文件中,因为这样可以让你设置合适的构建过程。
- 完全开源——我想要一个完全透明的库,以便之后的人可以修改它来适应他们的需求。这个库仅有的两个依赖是 .NET Framework 和 Newtonsoft.Json。你可以通过访问 OAuth2Login GitHub 仓库页面来了解,其余所有内容都在库中编写。
最后,我要感谢 ericzo 在几年前启动了这个库。当你访问 OAuth2Login GitHub 仓库时,你会看到这是他之前工作的分支。我大幅重写了大部分代码,但他仍然做出了巨大贡献;我怀疑如果我需要从零开始,这个版本的库就不会存在了。这就是开源的美妙之处。
如何设置 OAuth2Login 库
我真的努力让这个库易于使用。一旦你克隆了仓库,运行示例应该只需要两个步骤:
- 创建 Web.Oauth.config 并填入你的应用程序的值
- 运行解决方案,浏览器应该会打开一个页面,显示支持的登录提供商的按钮,你可以用来测试。
集成到你自己的解决方案中应该不会有太大差别。显然,你首先需要在你的 ASP.NET MVC 项目中引用 OAuth2Login 库,然后创建相应的配置,最后复制 OAuth2Login.Example 中的代码/类,以节省你一些实现时间。
既然你正在阅读这篇文章,我相信你已经知道如何在 Visual Studio 中引用项目了。那么,让我们开始处理最“复杂”的部分——配置库以供使用。
Web.Oauth.config 文件
让我们看看一个设置了多个 OAuth 提供程序的 Web.Oauth.config 文件示例。
<code><?xml version="1.0"?> <oauth2.login.configuration> <web failedRedirectUrl="~/AuthExternal/LoginFail" /> <oauth> <add name="Google" clientid="xxx_CLIENT_ID_xxx" clientsecret="xxx_CLIENT_SECRET_xxx" callbackUrl="https://:28950/AuthExternal/Callback/Google" scope="https://www.googleapis.com/auth/userinfo.email+https://www.googleapis.com/auth/userinfo.profile" /> <add name="Facebook" clientid="xxx_CLIENT_ID_xxx" clientsecret="xxx_CLIENT_SECRET_xxx" callbackUrl="https://:28950/AuthExternal/Callback/Facebook" scope="public_profile,user_friends,email" /> <add name="PayPal" clientid="xxx_CLIENT_ID_xxx" clientsecret="xxx_CLIENT_SECRET_xxx" callbackUrl="https://:28950/AuthExternal/Callback/PayPal" scope="openid email" endpoint="sandbox" /> <add name="Twitter" clientid="xxx_CLIENT_ID_xxx" clientsecret="xxx_CLIENT_SECRET_xxx" callbackUrl="http://127.0.0.1:28950/AuthExternal/Callback/Twitter" /> </oauth> </oauth2.login.configuration> </code>
我相信配置文件本身已经很明白了,但让我们解释一些可能存在问题的细节。
- failedRedirectUrl 是在出现问题时要将用户重定向到的 URL。例如,用户登录失败。或者用户拒绝授予对你应用程序的访问权限。
- 每个 OAuth 客户端都需要 clientId 和 clientSecret 值。当你创建应用程序时,你的 OAuth 提供商会向你提供这些值。
- 另一个必需的信息是 callbackUrl。这是你的 OAuth 提供商在用户登录后,连同适当的 OAuth 访问令牌一起重定向回你网站的 URL。
- 其他值取决于 OAuth 提供商。例如:
- 创建 Facebook 应用进行登录时,你需要指定用户授予你的哪些权限。
- 或者创建 PayPal 应用时,你可以在沙盒和生产环境之间进行选择,因此在测试时可以使用 endpoint="sandbox",或者在生产环境中删除该条目。
- 说到环境,这里有一个重要的说明。如果任何 OAuth 提供商的人看到这个——拜托,停止限制我们使用 2 个环境。大多数理解环境设置的开发者至少会设置 3 个环境:开发(本地机器)、暂存(用于测试的远程机器)和生产(网站运行的地方)。所以,如果你打算为单个应用程序提供不同的环境,并且你的开发者很差,让他们提供至少 3 个固定的环境而不是 2 个固定的环境。这样,没有暂存环境的人可以使用 2 个环境,而我们有 3 个环境的人则不需要创建和维护 2 个单独的应用程序。
获取 ClientId 和 ClientSecret
核心上,为每个 OAuth 提供商获取 ClientId 和 ClientSecret 都很简单。只有几个通用的步骤:
- 你访问 OAuth 提供商的开发者门户,注册账户并接受各种条款和条件。
- 通过提供所需数据来创建你的应用程序。确保准备好你的服务条款和隐私政策,因为他们会要求这些。
- 设置回调 URL。
- 从 OAuth 提供商那里接收 ClientId 和 ClientSecret。
此过程中的差异来自于 OAuth 提供商的要求以及其开发者门户的组织方式。此外,还有不同级别的应用审查。我在第 2 点提到的,你需要准备好服务条款和隐私政策——Facebook 实际上暂停了我的一个应用程序,因为它在网站上没有适当的隐私政策。其他 OAuth 提供商要么没有审查过该政策,要么对他们来说就足够了,但 Facebook 不行——应用程序被暂停了。
所以,让我给你提供一些链接,你可以在那里注册你的应用程序,并附带一些适用于不同 OAuth 提供程序的技巧和窍门。
Google OAuth
你需要访问 Google Developer Console 来开始。思路是创建一个新项目,启用 Google+ API(它负责登录),为其添加客户端凭据,设置适当的授权 JavaScript Origin 和授权 Redirect URI。列出它们:
- 凭据 -> 创建凭据 -> Oauth2 客户端 ID -> Web 应用程序
- 为你的客户端输入名称,例如“网站登录”。
- 授权 JavaScript Origin - 你的网站根目录。例如:http://www.mysite.com
- 授权重定向 URI - 你网站的回调 URL。例如:http://www.mysite.com/Callback/Google
- 将 Client ID 和 Secret 复制回 Web.Oauth.config。
Facebook OAuth
起点是 Facebook Developer 上的应用列表。当你创建应用程序时,只需转到设置 -> 添加平台 -> 网站。一旦你添加了网站并设置了站点的域,你就会得到 ClientId 和 ClientSecret。正如我在前面段落中所说,请确保准备好服务条款和隐私政策。他们可能会让你用占位符页面拖延一个月,但一定要在全面投入生产之前准备好这些页面。
PayPal OAuth
PayPal 重新设计了他们的开发者门户,新的版本从设计角度来看相当直接。但是,请注意——这个门户相当不稳定。
几个月前,有一个愚蠢的 bug,Dashboard 链接第一次点击时会重定向到主页。所以,为了在登录后看到你的应用程序,你需要:点击 Dashboard 链接,被重定向到主页,再次点击 Dashboard 链接,被重定向到主页,然后最后再点击一次 Dashboard 链接。我没开玩笑。我花了大约一个小时才弄清楚如何创建一个新应用程序,因为 Dashboard 链接出现了问题。
他们在门户上的最后一个 bug 花了我很多时间,那就是更新应用程序设置需要非常长的时间。我尝试更改用户所需的权限和回调 URL 时发现了这一点。我在 PayPal Developer Portal 为我的应用程序更改了这些设置,然后花了 3 个小时试图弄清楚为什么这些更改没有生效。绝望地想让它工作,我用我的新设置创建了一个新应用程序,在 Web.Oauth.config 中更改了 ClientId 和 ClientSecret——搞定,立即奏效。最大的震惊来自我花时间向 PayPal Developer Support 提交这个 bug——他们说——哦,我们知道那个,这是预期行为。你需要等待我们的缓存刷新……等待 24 小时应该就能工作。
我列出所有这些不是为了抱怨,而是为了警告你。如果你在使用 PayPal Developer Portal 时遇到问题——别担心,你不是一个人。至少,有生以来第一次,那句熟悉的话——“不是我的代码,是外部 bug”——将是真实的。
Twitter OAuth
你需要访问 Developer Apps 页面。有两个大陷阱:
-
这可能是唯一一个众所周知的 OAuth 提供商,它不支持 Callback URL 中的 localhost。你需要使用 127.0.0.1 来代替。这也意味着如果你使用 IIS Express 进行调试,你需要配置它监听所有地址。要做到这一点,打开你的 IIS Express 配置文件(%MyDocuments%/IIS Express/config/applicationhost.config),找到与我提供的示例网站相关的条目(搜索端口 28950),然后在现有的 localhost 条目下方添加一个额外的行。
<code> <binding protocol="http" bindingInformation="*:28950:localhost" /> <binding protocol="http" bindingInformation="*:28950:*" /> </code>
-
获取用户的电子邮件不直接支持(他们一直在研究这个问题)。关于这个主题的有趣阅读——https://twittercommunity.com/t/how-to-get-email-from-twitter-user-using-oauthtokens/558/22
解释演示和代码结构
到目前为止,希望你已经成功地在下载的源代码中的演示项目中创建了 Web.Oauth.config,按了 F5,尝试了不同的登录提供商,现在你可能会想:好吧,你能详细解释一下代码到底是如何工作的,以及我如何将其应用到我自己的网站上吗?
我们来分解一下。
- 演示网站中的所有内容主要围绕 AuthExternalController。你很可能想将该控制器及其 Views/AuthExternal 目录复制到你的网站中。
- 点击 Home/Index 视图中的按钮会打开一个新的弹出窗口,该窗口会导航到类似以下的 URL:'/AuthExternal/Login/Facebook?mode='。通过这个示例 URL,AuthExternalController 的 Login 方法将接收到以下值:
- id=Facebook - 这意味着 BaseOauth2Service.GetService(id) 方法将返回一个 FacebookService 实例来处理认证。
- mode=Default - 如果你将来想支持用户将多个登录提供商添加到其帐户的场景,你可能需要将此模式设置为“AttachLogin”。目前,空值或“Default”将完成工作。
- 一旦你获得了 BaseOauth2Service 的实例,BeginAuthentication() 方法将生成用户将被重定向到的 URL。一旦用户同意与你的应用程序共享其登录信息,OAuth 提供商将调用 Callback 方法。
- 在 Callback 方法中,我们再次实例化适当的 BaseOauth2Service(取决于 id),并尝试提取 OAuth 访问令牌(如果需要,还有密钥)以及其他用户信息(电子邮件、电话号码等)。一旦我们有了这两个关键信息,我们就可以为我们的用户创建一个帐户,并通过设置 AuthCallbackResult.RedirectUrl 来告诉我们的弹出窗口应该去哪里。
- 如果你打开 Callback.cshtml,你会看到重定向脚本首先尝试通过 Javascript 将登录通知传递给窗口打开者。你可以利用这一点来提供自己的自定义 loginMethods.handleRedirect 方法。
至于 OAuth2Login 库,让我们按顺序浏览命名空间:
- Oauth2Login.Client - 这些主要是遗留类,我将在下一次重构中尝试删除它们。基本上,它们在实例化后存储来自配置文件的数据以及与 OAuth2 提供商通信期间获得的数据。
- Oauth2Login.Configuration - 用于读取 Web.Oauth.config 文件的类。
- Oauth2Login.Core - 库内部和外部使用的基础类和核心类。
- Oauth2Login.Service - 主要命名空间,包含每个 OAuth2 提供商的类。BaseOauth2Service 类提供基本功能,然后每个类根据提供商提供适当的调用。
- Oauth2Login.ServiceData - 用于与 API 交互的额外数据类放在这里。目前没什么大的。
我希望在阅读了这些段落并花几分钟浏览了源代码之后,你能说:哇,这似乎代码量不大。因为这也是另一个目标——保持代码库尽可能小。这样,如果 API 有任何更改,将更容易将其实现回库中。
关于库的一些说明
如果你打算使用该库,请注意(一旦我有时间),我计划重构该库以使其更加精简。所以,不要因为破坏性更改而感到惊讶。
另外,如果可以的话,请将库放在 git 仓库下,并考虑提交 pull request。例如,现在基本结构已经就位,我认为测试和添加更多 OAuth2 提供商应该很容易。如果有人这样做,请通知我并提交 pull request——我很乐意合并你的贡献并给予你应得的荣誉和宣传。
就这么多了?!
是的,基本上就是这样。OAuth2 是一个简洁的协议,可惜的是它的简单性、美观性和实用性却隐藏在许多糟糕的库/SDK 后面,这些库/SDK 包含 tons 的不必要代码。
回想起来,我真的很高兴受到了上述书籍(别让我思考:网页可用性常识指南&&告别文字:写作有效的网页内容)的启发,并开始研究所有那些用于 OAuth2 登录集成的臃肿 SDK 的代码背后到底发生了什么。
展望未来,我只能希望我的工作能对其他开发者有所帮助,并且这个小小的 OAuth2Login 库能不断发展。
未来计划
我发现了一篇非常有用的文章,讨论了互联网上特定社交登录提供商的受欢迎程度。从数据来看,Yahoo 和 LinkedIn 值得实现,我接下来将这样做。如果你能抢先一步——考虑提交 pull request。