本文为学习多篇OAuth 2.0文章后总结。
具体授权码模式请看 阮一峰老师讲解 OAuth 2.0 的四种方式
https://tools.ietf.org/html/rfc6749
https://www.ixigua.com/6841902954690118155
1. 授权码 - authorization code
客户端换取授权码,客户端调用服务端使用授权码换token,客户端调用服务端使用token访问资源
必须要有服务端
- 授权码模式是四种模式中最繁琐也是最安全的一种模式。
- client_id 存储在前端
- client_secret 存储在服务器
- 支持refresh token
- 使用场景:第三方Web服务器端应用与第三方原生App,例如头条微信授权登录
授权码请求
response_type:(必传)此时为"code"
appid: (必传)微信服务器下发的标识第三方应用的
client_id: (必传)微信服务器下发的标识第三方应用的,和appid根据要求可选
redirect_uri: (必传)授权成功后的重定向地址
scope:(可选)标识授权范围
state:(推荐)第三方应用提供的一个字符串,微信授权服务器会原样返回
1.请求授权码(示例)
GET /authorize?
response_type=code&
appid=wxe9199d568fe57fdd&
client_id=wxe9199d568fe57fdd&
state=wilson&
redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Foauth2&
scope=user,photo HTTP/1.1
2.授权码根据重定向地址返回 (前端地址?后台api?)
HTTP/1.1 302 Found
Location: https://client.example.com/oauth2?code=SplxlOBeZQQYbYS6WxSbIA&state=wilson
3.拿到授权码以后,在 后端服务,直接向 微信授权服务器 请求令牌
https://xeixin.com/oauth/token?
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET&
grant_type=authorization_code&
code=AUTHORIZATION_CODE&
redirect_uri=CALLBACK_URL
案例:https://www.toutiao.com/a6643594372862444040/
2. 授权码 "隐藏式" - implicit
客户端让用户登录授权服务器换token,客户端使用token访问资源(只有客户端,没有服务端)
- 支持refresh token
- 使用场景:为web浏览器应用设计。一般简化模式用于没有服务器端的第三方单页面应用,因为没有服务器端就无法使用授权码模式。
https://b.com/oauth/authorize?
response_type=token&
client_id=CLIENT_ID&
redirect_uri=CALLBACK_URL&
scope=read
3. 密码式 - password
用户在客户端提交账号密码换token,客户端使用token访问资源
- 不支持refresh token
- 使用场景:为遗留系统设计。这种模式十分简单,但是却意味着直接将用户敏感信息泄漏给了client,因此这就说明这种模式只能用于client是我们自己开发的情况下。因此密码模式一般用于我们自己开发的,第一方原生App或第一方单页面应用。
https://oauth.b.com/token?
grant_type=password&
username=USERNAME&
password=PASSWORD&
client_id=CLIENT_ID
4. 客户端凭证 - Client Credentials Grant
客户端使用自己的标识换token,客户端使用token访问资源
- 不支持refresh token
- 使用场景:为后台api服务消费者设计。一般用来提供给我们完全信任的服务器端服务。
https://oauth.b.com/token?
grant_type=client_credentials&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET
如何在移动App中使用OAuth 2.0?
移动 App 可以分为两类,一类是没有 Server 端的 App 应用,一类是有 Server 端的 App 应用。
这两类 App 在使用 OAuth 2.0 时的最大区别,在于获取访问令牌的方式:
- 如果有 Server 端,就建议通过 Server 端和授权服务做交互来换取访问令牌;
- 如果没有 Server 端,那么只能通过前端通信来跟授权服务做交互,比如隐式许可授权类型。当然,这种方式的安全性就降低了很多。也可以将一个“迷你”的 Web 服务器嵌入到 App 里面去,这样就可以像 Web 应用那样来使用 OAuth 2.0 。这样的 App 通过监听运行在 localhost 上的 Web 服务器 URI,就可以做到跟普通的 Web 应用一样的通信机制。
这里介绍另外一种 PKCE 协议,全称是 Proof Key for Code Exchange by OAuth Public Clients。在下面的流程图中,为了突出第三方软件使用 PKCE 协议时与授权服务之间的通信过程,我省略了受保护资源服务和资源拥有者的角色:
首先,App 自己要生成一个随机的、长度在 43~128 字符之间的、参数为 code_verifier 的字符串验证码;接着,我们再利用这个 code_verifier,来生成一个被称为“挑战码”的参数code_challenge。那怎么生成这个 code_challenge 的值呢?OAuth 2.0 规范里面给出了两种方法,就是看 code_challenge_method 这个参数的值:
- 一种 code_challenge_method=plain,此时 code_verifier 的值就是 code_challenge 的值;
- 另外一种 code_challenge_method=S256,就是将 code_verifier 值进行 ASCII 编码之后再进行哈希,然后再将哈希之后的值进行 BASE64-URL 编码,如下代码所示。
code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
在第一步获取授权码 code 的时候,我们使用 code_challenge 参数。需要注意的是,我们要同时将 code_challenge_method 参数也传过去,目的是让授权服务知道生成 code_challenge 值的方法是 plain 还是 S256。
https://authorization-server.com/auth?
response_type=code&
app_id=APP_ID&
redirect_uri=REDIRECT_URI&
code_challenge=CODE_CHALLENGE&
code_challenge_method=S256
在第二步获取访问令牌的时候,我们使用 code_verifier 参数,授权服务此时会将 code_verifier 的值进行一次运算。那怎么运算呢?就是上面 code_challenge_method=S256 的这种方式。
没错,第一步请求授权码的时候,已经告诉授权服务生成 code_challenge 的方法了。所以,在第二步的过程中,授权服务将运算的值跟第一步接收到的值做比较,如果相同就颁发访问令牌。
POST
https://api.authorization-server.com/token?
grant_type=authorization_code&
code=AUTH_CODE_HERE&
redirect_uri=REDIRECT_URI&
app_id=APP_ID&
code_verifier=CODE_VERIFIER
现在,你就知道了我们是如何使用 code_verifier 和 code_challenge 这两个参数的了吧。总结一下就是,换取授权码 code 的时候,我们使用 code_challenge 参数值;换取访问令牌的时候,我们使用 code_verifier 参数值。
Keycloak介绍
Keycloak是为现代应用和服务提供了开源IAM(Identity and Access Management)解决方案
https://www.keycloak.org/
openid/AppAuth-Android