OIDC & OAuth2.0 协议及其授权模式详解|认证协议最佳实践系列【1】
OIDC / 是一种开放的标准,可以帮助应用程序安全地访问用户的资源,而无需将用户的凭据(如用户名和密码)暴露给应用程序,我们可以通过标准协议,建立集中的用户目录和统一认证中心,将内外部业务系统的登录认证统一到认证中心,实现集中化的管理,从而避免每套业务系统都要搭建一套用户体系所造成的管理侧不便及安全侧的风险。
本文将带各位详细了解 OAuth & OIDC 及其授权模式。
01.协议介绍
【资料图】
OAuth & OIDC
OAuth 是一个授权框架,使应用程序能够获得对 HTTP 服务上用户帐户的有限访问权限,例如 Facebook、GitHub 和 DigitalOcean。它通过将用户认证委托给托管用户帐户的服务,并授权第三方应用程序访问用户帐户来实现。
OpenID Connect (OIDC) 是建立在 OAuth 框架之上的简单身份层。它在 OAuth 提供的授权的基础上添加了认证。
初看上面这段话你可能很难理解,这里用白话解释下,OAuth 在设计之初,是为了 API 安全的问题,它是一个授权协议,而 OIDC 则在 协议的基础上,提供了用户认证、获取用户信息等的标准实现,当然我们也可以理解获取用户信息也是一个 API ,用 OAuth 也没问题的,考虑到读者的感受,在这里不要过于纠结,只要记住 OIDC 是完全兼容 的,我们现在也推荐用 OIDC。
术语介绍
啥啥啥,写的这都是啥,小白看到这些脑袋都大了,我在这里重点说下 OIDC/ 协议交互时所参与的几个角色,等你对协议熟悉了,可以反过头来再看下相关的介绍,在接下来的授权模式介绍中,我们会结合这四个角色,介绍下不同授权模式的流程。
资源持有者(End User / Resource Owner) 终端用户
应用/客户端(Client)应用的前端,可以是 web 端、App 端、移动端应用
资源服务器(Resource Server) Client 对应的后台
认证服务器(OpenID Provider / Authorization Server)即 Authing
Client 类型介绍
/ OIDC 中定义了 2 种 Client 类型:
我们在这里解释下:
Confidential Clients 机密型应用:能够安全的存储凭证(client_secret),例如有后端服务,你的前端是 Vue,后台是 Java ,那么可以理解为机密性应用,因为你的后端能够安全的保存 client_secret,而不会将 client_secret 直接暴露给用户,此时你可以使用授权码模式。
Public Clients 公共型应用:无法安全存储凭证(Client Secrets),例如 SPA 、移动端、或者完全前后端分离的应用,应当使用授权码 + PKCE 模式
OIDC 授权模式与选型建议
重点来啦!我们要了解授权模式,才能更好的针对系统类型进行授权模式的选型,避免由于授权模式选型不当所造成的开发工作增加和安全侧的漏洞。
授权模式
选型建议
02.授权模式详细介绍
授权码模式(Authorization Code)
授权码模式适合应用具备后端服务器的场景。授权码模式要求应用必须能够安全存储密钥,用于后续使用授权码换 Access Token。授权码模式需要通过浏览器与终端用户交互完成认证授权,然后通过浏览器重定向将授权码发送到后端服务,之后进行授权码换 Token 以及 Token 换用户信息。
整体上,有以下流程:
1. 在你的应用中,让用户访问登录链接,浏览器跳转到 Authing,用户在 Authing 完成认证。
2. 浏览器接收到一个从 Authing 服务器发来的授权码。
3. 浏览器通过重定向将授权码发送到你的应用后端。
4. 你的应用服务将授权码发送到 Authing 获取 AccessToken 和 IdToken,如果需要,还会返回 refresh token。
5. 你的应用后端现在知道了用户的身份,后续可以保存用户信息,重定向到前端其他页面,使用 AccessToken 调用资源方的其他 API 等等。
流程图如下:
授权码 + PKCE 模式(Authorization Code With PKCE)
如果你的应用是一个 SPA 前端应用或移动端 App,建议使用授权码 + PKCE 模式来完成用户的认证和授权。授权码 + PKCE 模式适合不能安全存储密钥的场景(例如前端浏览器) 。
我们解释下 code_verifier 和 code_challenge。
code_verifier:在 [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~" 范围内,生成43-128位的随机字符串。
code_challenge:则是对 code_verifier 通过 code_challenge_method 例如 sha256 转换得来的。
用大白话讲下就是在认证是用户携带的是加密后的 code_challenge ,在用户认证成功获取 Token 时,客户端证明自己的方式则是把 code_verifier 原文发送,认证中心收到获取 Token 请求时通过 code_verifier + code_challenge_method 进行转换,发现最终结果与 code_challenge 匹配则返回 Token ,否则拒绝。
整体上,有以下流程:
1. 在你的应用中,让用户访问登录链接(包含 code_challenge ) ,浏览器跳转到 Authing,用户在 Authing 完成认证。
2. 浏览器接收到一个从 Authing 服务器发来的授权码。
3. 浏览器通过重定向将授权码发送到你的应用前端。
4. 你的应用将授权码和 code_verifier 发送到 Authing 获取 AccessToken 和 IdToken,如果需要,还会返回 Refresh token。
5. 你的应用前端现在知道了用户的身份,后续使用 Access token 换取用户信息,重定向到前端其他页面,使用 AccessToken 调用资源方的其他 API 等等。
流程图如下:
客户端凭证模式(Client Credentials)
Client Credentials 模式用于进行服务器对服务器间的授权(M2M 授权),期间没有用户的参与。你需要创建编程访问账号,并将 AK、SK 密钥对交给你的资源调用方。
注意⚠️:Client Credentials 模式不支持 Refresh Token。
整体上,有以下流程:
1. 资源调用方将他的凭证 AK、SK 以及需要请求的权限 scope 发送到 Authing 授权端点。
2. 如果凭证正确,并且调用方具备资源权限,Authing 为其颁发 AccessToken。
流程图如下:
隐式模式(Implicit)(不推荐)
隐式模式适合不能安全存储密钥的场景(例如前端浏览器),不推荐此模式,建议采用其他模式。在隐式模式中,应用不需要使用 code 换 token,无需请求 /token 端点,AccessToken 和 IdToken 会直接从认证端点返回。
注意⚠️:因为隐式模式用于不能安全存储密钥的场景,所以隐式模式不支持获取 Refresh Token。
整体上,有以下流程:
1. 在你的应用中,让用户访问登录链接,浏览器跳转到 Authing,用户在 Authing 完成认证。
2.Authing 将浏览器重定向到你的应用回调地址,AccessToken 和 IdToken 作为 URL hash 传递。
3. 你的应用从 URL 中取出 token。
4. 你的应用可以将 AccessToken 与 IdToken 保存,以便后续使用,例如携带 AccessToken 访问资源服务器,携带 IdToken 请求服务端从而服务端能够辨别用户身份。
流程图如下:
密码模式(Password)(不推荐)
不推荐使用此模式,尽量使用其他模式。只有其他模式都无法解决问题时才会考虑使用密码模式。如果使用密码模式,请确保你的应用代码逻辑非常安全,不会被黑客攻击,否则将会直接泄露用户的账密。一般用于改造集成非常古老的应用,否则绝对不要把它作为你的第一选择。
整体上,有以下流程:
1. 你的应用让用户输入账密信息。
2. 你的应用将用户账密发送到 Authing 。
3. 如果账密正确,Authing 返回 token 。
流程图如下:
设备代码模式(Device Code)(几乎用不到)
对于一些连接到互联网的输入受限设备,设备不会直接验证用户身份,而是让用户通过链接或二维码转到手机或电脑上进行认证,从而避免了用户无法轻松输入文本所带来的糟糕体验。
Device Code Flow 与前面几个不太一样,开始不再是由资源持有者发起,而是由客户端开始。甚至登录的方法与客户端还没有特别的关联。
大致流程说明如下:
1. 客户端发起向认证服务器取得 device_code 和user_code 。
2. 客户端通过二维码或者其他方式将 user_code交给资源持有者。
3. 资源持有者透过某个端点 (endpoint) 与user_code 进行认证。
4. 客户端通过 device_code 轮训认证服务器是否有人认证,若有人认证则会返回 access_token 。
流程图如下:
一个设计良好的系统,都需要一个标准、安全、可扩展的用户认证协议,无论是 ToC 还是 ToE ,接来下我们还会针对具体的授权模式结合实际场景讲一下具体的模式。
03.本章总结
1.本章我们介绍了 OIDC 和 两个协议,在使用起来,这两个协议并没有太大的区别, 是一个授权协议,OIDC 协议则是在 的提供的授权的基础上添加了认证。
2.授权模式,我们分别介绍了授权码模式(Authorization Code) 、授权码 + PKCE 模式(Authorization Code With PKCE) 、客户端凭证模式(Client Credentials) 、隐式模式(Implicit)(不推荐)、密码模式(Password) (不推荐)、设备代码模式(Device Code)(几乎用不到) 。
3. 授权模式选择
如果我们的应用是 WEB 端应用,那我们应该选择授权码模式(Authorization Code)
如果我们是原生应用,例如 SPA ,移动端,那我们应该选择授权码 + PKCE 模式(Authorization Code With PKCE)
如果我们是想把应用的 API 给第三方服务安全的调用,则需要选择客户端凭证模式(Client Credentials)
接下来我们还会详细介绍授权码模式(Authorization Code)、授权码 + PKCE 模式(Authorization Code With PKCE)、客户端凭证模式(Client Credentials)如何接入 Authing。敬请期待!