时至今日,任何开发者都无法忽视OAuth 2 的巨大影响力,无论如何,你都会有机会和 OAuth 2 有交锋。但问题在于,如果你曾经试图搜索过关于OAuth 2 的资料,网上零零散散众说纷纭的内容一定会让你感到困惑和不解。即使你静下心来尝试阅读由IETF的OAuth工作组权威制定和开发的RFC规范,一般的,RFC 6749(https://www.rfc-editor.org/rfc/rfc6749, The OAuth 2.0 Authorization Framework)会是一个好的起点和选择。但这一定会让你抓狂,因为OAuth工作组开发和制定的RFC规范充满了一堆专业的术语和名词,我相信这些术语和名词即使对母语是英语的阅读者也充满了挑战,它仿佛一篇专业的学术论文,让试图在其中寻找答案的开发者不知所措。即使你耐心读完这份75页的规范,你发现你深陷其中,因为它引出了更多的规范等待你来探索。事实上,OAuth工作组制定了一系列的规范,这些规范涉及和覆盖 OAuth 的方方面面,例如安全、最佳实践、在无浏览器的设备上如何使用和实践OAuth等(你可以参阅https://oauth.net/specs/获取OAuth工作组制定和开发的各种规范)。如下图所示,你仿佛置身一座迷宫当中,如果你想构建一个安全和全面的授权服务器,这些规范是你无法绕过的障碍。
在这个专栏里我将为你拨云见日,揭开 OAuth 和 OIDC 的神秘面纱。这一次,我带你打怪升级,一次性掌握关于 OAuth 和 OIDC 的一切。
你即将要开启怎样的一段旅程
如下面的“地图”所示。想象你是一个想要拯救美丽公主的超级马里奥。你需要一路过关斩将才能最终到达皇冠的位置拯救公主“抱得美人归”。整个地图可以分为七个关卡和两条主线,其中,第一关到第三关是第一条主线——OAuth 的世界,第四关和第五关是另一条主线——OIDC 的世界。你在 OAuth 的世界里收获的经验和知识会指导和帮助你轻松闯关 OIDC 的世界。第六关是整个地图的最后一部分,是对整个“地图”的一个小的延伸和总结。在经历前两条主线地图的磨练后,你已经变成一个成熟强大的马里奥——成为 OAuth 2和 OIDC 领域的“小专家”,最后的这一部分是系统对你的“奖励”,你可以“闲庭信步”,享受我为你呈现的额外的“知识大餐”。
第零关
这是整个“征程”的开始,这一关卡是对整个地图的梗概。在这里你会知道到整个地图有两条主要线路——OAuth 和OIDC。你会了解到它们是什么以及诞生背景和发展历程,同时你还会收获一些“闯关秘籍” —— 一些重要的参考文档。
OAuth的世界
从第一关开始,你将正式进入 OAuth 的世界,在这里你会了解到关于 OAuth 的一切。
第一关
这一关是 OAuth 的基础理论篇,包含九个小节。在这里你会学习到关于 OAuth 的如下知识:
- 四大角色。授权服务器(authorization server)、资源服务器(resource server)、资源拥有者(resource owner)、客户端(client)
- “四大”授权类型。授权码模式(authorization-code)、客户端模式(client credentials)、密码式(password)、简化模式(implicit)。值得注意的是从 OAuth 2.1 开始密码模式和简化模式已经被完全废弃,不再列入规范。这也是 OAuth 2.1 相对于 OAuth 2.0 最大的变化和革新,但是作为补充和说明我们还是讨论和说明这两种授权模式。笔者也了解到,在国内的实践中很少有开放平台会对第三方应用程序开放密码模式和简化模式,它们在以往的实践中已经被验证是不安全或充满不确定性的。所以,如果你在实践中有使用这两种模式,我建议你尽快升级成更安全的授权码模式。
- PKCE。RFC 7636 文档定义了一种防止授权码被拦截的技术,它就是 PKCE。在这里你会了解到什么是 PKCE,以及它是如何防范授权码拦截攻击的。我们自定义实现的授权服务器也会支持 PKCE 技术。
- JOSE规范。JOSE 全称 JSON Object Signing and Encryption ( JSON 对象签名和加密),它定义了一系列的标准,用来规范网络传输过程中使用 JSON 的方式。我会着重向你介绍JOSE标准族的 JWT、JWS 和 JWE 的内容以及简要介绍关于 JWK 和 JWA 的内容。
- 其他关于 OAuth 2 的知识,比如 scope、令牌和客户端注册等。
第二关
在第一关中,你已经了解了 OAuth 2 的绝大部分知识,这之后将进入第二关。这一关也是整张地图最为困难和复杂的部分。在这一关我们将利用前面的知识手把手带你实现一个生产可用的完全遵循 OAuth 2.0 规范的授权服务器以及资源服务器,同时我会向你呈现如何在公共客户端和加密客户端中接入 OAuth 2 授权服务器。我们实现的授权服务器将会包含以下端点:
- Authorization Endpoint(授权端点)。负责接受授权请求并生成授权码(Authorization Code)。该端点并不会对所有授权类型生效,密码模式或客户端模式无需调用该端点。
- Token Endpoint(令牌端点)。负责生成令牌,包含访问令牌和刷新令牌,并将它们以JSON对象的形式返回给请求者。
- Token Introspection Endpoint(令牌校验端点)。令负责校验已经生成和颁发的令牌的有效性。
- Token Revocation Endpoint(令牌取消端点)。负责取消已经生成和颁发的令牌。
- OAuth2 Authorization Server Metadata Endpoint(授权服务器元信息端点):OAuth2 规范并没有明确规约各个端点的路径,因此在实践中 OAuth 授权服务器对外暴露接口的路径可能各不相同(在详细介绍时我将向你展示各个开放平台中端点路径的差异)。除了端点路径授权服务器可能会暴露一些其他信息,比如免责声明、归属声明或授权服务器能力说明等。
- JWK Set Endpoint。当使用基于JWT的访问令牌时,授权服务器需要向客户端暴露验证JWT的公钥的端点。
第三关
在经历过前面的两关后,相信我们已经对 OAuth 有了全面的认识——它是什么、它解决什么问题、它是如何运作的,以及如何实现 OAuth 2 四个角色。那么在第四关我们首先会讨论一个重要的议题——OAuth 的安全性及其最佳实践。OAuth 2 是一把双刃剑,在懂得正确使用它的人手中它才是“神兵利器”,否则稍有不慎就会带来灾难性的结果。 在第三关另外一个议题是如何在移动应用和本地应用程序以及在没有浏览器的设备(如电视、游戏机或打印机等)上使用OAuth。这分别也是RFC 8252和RFC 8626定义的内容。在这一关我同时会对我们的授权服务器做一个额外的优化——让我们的授权服务器支持多种客户端认证方式(包括client_secret_basic、client_secret_post、client_secret_jwt等)以及多种响应模式(query、fragment以及form_post等模式)。
OIDC的世界
从第四关开始,你将进入OIDC的世界,在这里你会了解到关于OIDC的一切。正如你在图中看到的,OIDC的关卡很少,这是“抱得美人归”的捷径。但它的简单是基于你对OAuth 2的理解和掌握。简而言之,OIDC是构建在OAuth 2之上的身份认证层。你越了解OAuth 2,你就越容易掌握OIDC。
第四关
第四关是OIDC的理论知识篇,在这一关里你将会学到如下内容:
- 什么是OIDC以及OIDC的三大身份认证流(授权码流、简化流以及混合流);
- 什么ID令牌;
- OIDC规范的四个标准scope(profile、email、address、phone);
- 如何请求和获取用户信息。
第五关
这一关我们讲解 OIDC 的实战,你将学到:
- 扩展我们在OAuth2路线里实现的授权服务器使其支持OIDC协议;
- 使用OpenID Connect保护基于React的单页面应用程序;
- OpenID Connect和服务端应用程序集成;
- 如何登出你的应用。
第六关
这一关是整个专栏的总结和扩展篇,在这里我会向你介绍开源社区一些优秀的授权服务器实现,我会提供如何构建和实现这些授权服务器的方法。同时我会向你介绍我在下文第(1)小节向你提到另一门联合身份认证的重要技术——SAML 2.0。它虽然不是我们故事的主线,但是它仍然充满活力且应用广泛,因此在这一关我向你简要介绍这一技术的细节。下面是在这一关里包含的内容:
- OAuth 2.0为什么被称为授权框架;
- 关于授权服务器的实现还有哪些优秀的选择;
- SAML 2.0原理和实战。
你将会收获什么
整个专栏采用理论和实践相结合的方式,我们实现的授权服务器角色也完全遵循OAuth 2.0规范。如下图所示,对于关键代码的实现,我都为你标明了RFC文档和专栏对应章节的地址,力求做到所有的实现都有理有据,有章可循。
在本专栏结束的时候你可以收获以下内容:
- 关于OAuth 2.0的绝大多数知识。
- 关于OIDC的绝大多数知识。
- 如何构建一个安全的生产可用的授权服务器。
- 关于实现授权服务器过程中的一些细节,例如端点的命名、授权请求中scope参数传递和不传递的不同处理、如何构建错误响应结果等。
- 在移动应用程序中或输入受限的设备中使用OAuth 2.0的细节。
- 如何构建一个资源服务器。
- 如何安全快速地接入授权服务器。
- OAuth 2.0 Web API设计的最佳实践。
- 授权服务器支持多种客户端认证方式,包括client_secret_basic、client_secret_post、client_secret_jwt、private_key_jwt等客户端认证方式。
为什么我会写这个专栏
如果你曾经和 OAuth 2 或者 OIDC 有过交锋,且这次初遇是不愉快的,比如说总是傻傻分不清的认证和授权,OAuth 2 和 OIDC,它们是什么,它们的存在又在各自解决什么问题。 笔者也曾有过这样的经历,我曾经有幸主导过一个大型开放平台的建设,一开始笔者不以为然,以为它和我曾经构建过的复杂应用并无异样,但是越是了解,我就越被“困在OAuth这座迷宫中”无法自拔。我实现的授权服务器安全么?客户端认证方式该如何选择?应该不应该给客户端授予刷新令牌?怎么实现一个安全的资源服务器?我提供的开放文档该如何构建才能方便客户端开发者接入?这些问题一度让我如履薄冰,因为安全无小事,一旦某个细节出现漏洞将会导致重大的问题,因此笔者潜心钻研,而到目前为止,由我主导实现的授权服务器,一直安全稳定地运行。 构建这个专栏的目的,是将我在OAuth领域的知识和经验进行总结和传播,我相信跟着笔者的思路,一幅关于OAuth 2和OIDC的地图将会徐徐为你展开,最终你将会成为OAuth 2和OIDC领域的“小专家”。同时学海无涯,三人行必有我师,我也期望你与我探讨某些细节,共同完善和优化这些内容。
内容总结
下面我以一张图说明本专栏的所有内容。