首先,从高维度看,OAuth 是要解决什么问题?
OAuth 要解决的是客户端在不知道和不使用用户密码的情况下,怎么样安全访问并获取用户所拥有的资源的问题。
顺带说一句,经常和 OAuth 一起谈到的 OIDC 则是解决了用户信息(profile)获取的问题。简单说,OAuth 解决了 Authorization 的问题,OIDC 解决了 Authentication 的问题。
OAuth 有几种角色:
- Resource Owner:资源的拥有者,比方说终端用户;
- Resource Server:资源服务器,比如用户想要调用的 API;
- Client:就是用户来进行鉴权和资源操作的客户端,比方说浏览器或者 CLI;
- Authorization Server:就是专门授权的服务器。
所以 OAuth 就是用户想通过某种机制,通过 Client 和 Authorization Server 交互来获得能够访问资源的 token。
对于 OAuth2.0 的授权流程,可以根据 grant type 来做个归类。下面的图示全部都来自 Auth0 的官方文档。
1. Authorization Code:用户访问 web app,web app 的后端请求 auth server,于是重定向到登录页面,用户输入登录信息,auth server 就给 web app 一个授权码,这个授权码通过 callback URL 返回,而这个参数一般放在这个 url 的参数中,比如:http://…/callback?token=TOKEN。之后 web app 就可以拿着授权码去取 token 了。这种方式不需要用户这边存放任何 secret,但是需要用户参与 consent,并且具备 web app 的后端,因为和 auth server 的交互主要都是 web app 后端完成的。

但是这种方法存在一些 concern,比如说,这个 code 如果在返回途中被截获怎么办,截获者就可以使用这个 code 来获取 token 了。
2. PKCE:对于上述问题,有一个改进的办法,就是使用 PKCE(Proof Key for Code Exchange)来给它增强。基本原理是,客户端生成一个随机字符串 code verifier,根据一个算法 code challenge method 来生成它的 hash(challenge),获取授权码 code 的时候需要把这个 method 和 challenge 带过去;接着,code 正常返回,但之后客户端拿着 code 去获取 token 的时候,需要带上这个 code verifier,这样 auth server 就可以根据之前拿到的 method 和 challenge,以及刚得到的 code 和 code verifier,来校验用户是不是可以得到这个 token。
在这种情况下,如果 code 被劫持,那么对方拿到了 code,却没有 code verifier,也就没什么用。

这种方法是比较推荐的,对于一些 CLI 登录使用这种方法的时候,重定向 URL 可以是一个带有 code 的指向 localhost 的地址以被 CLI 捕获。
3. Device Code:前面说到的 Authorization Code 这种方法还有一个变体,就是对于一些需要用户参与,但是又没有浏览器(或者自动打开浏览器)的场景下,这个重定向到浏览器来获取用户 consent 的过程,被其它方式来取代,这种变体可看做名为 Device Authorization 的流程:

可以看到,上面的浏览器重定向的过程被替换成了返回 code 和 verification url,然后用户使用 verification URL 加上这个 user code 来完成 consent 的过程,在这个过程完成之后,这个 device 才被授权。在这个过程完成之前,需要 app 不断去 poll 检查是不是 device 已经被授权了。
4. Client Credential:前面说到的 Authorization Code 虽然好用,但是需要用户手动登录确认的过程,对于一些没有人参与的 M2M(machine-to-machine)系统而言,这是不现实的。因此在 client id 的基础上,再加上一个 client secret,一样可以完成 auth 的流程。这种场景其实就相当于是 client 和 resource owner 是同一个了:

5. Implicit:这种其实就是上面 Authorization Code 的简化版,去掉了 code 的环节,直接发 code。这种方式在 app 只有前端的场景下(比如 SPA)使用,因为它没法进行后端和 Auth Server 的通信。但是这种方式因为安全性低,因而不推荐,因为即便是 SPA,还是可以用前面说的那种 PKCE 增强的 Authorization Code 方式来实现。

再来看这个 callback 的 URL,和前面提到的 Authorization Code 流程不一样的是,它返回的 token 放在 URL 的 fragment 里面,而不是 query 里面,比如:http://…/callback#token=TOKEN,这样做的好处是这个 TOKEN 不会在浏览器跳转的时候送到服务器,就不容易泄露。
6. Password:这种方式其实就是让 client 获知用户的用户名和密码,属于风险比较大的做法,要求这个 app 是用户百分百信任的——这也就是说,它没有解决 OAuth 本身应该解决的问题,因此很少使用。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》