【目录】
- OAuth2 框架模型
- AccessToken:访问令牌
- RefreshToken:刷新领牌
- OAuth Scope: 作用域
- 四种授权模式
- Authorization Code:授权码
- Client Credentials:凭证式
- Legacy: Implicit Flow (隐式)
- Legacy: Password Grant(密码式)
一、OAuth 2.0 Framework
1、AccessToken
OAuth访问令牌是OAuth客户端用于向资源服务器发出请求的字符串。
OAuth规范并没有对访问令牌的格式特殊约定,实际上,各种OAuth服务器为其访问令牌选择了许多不同的格式。
访问令牌可以是“Bearer Tokens"或“sender-constrained”令牌,后者要求OAuth客户端以某种方式证明拥有私钥,以便使用访问令牌,否则令牌无效。
//A 网站拿到令牌以后,就可以向 B 网站的 API 请求数据了。
curl -H "Authorization: Bearer ACCESS_TOKEN" "https://api.b.com"
拥有多属性的访问令牌是OAuth安全模型的基础:
- OAuth Client不能读取或解释访问令牌。OAuth Client不是令牌的目标受众。
- 访问令牌不会向OAuth客户端传递用户身份或有关用户的任何其他信息。
- 访问令牌应仅用于向资源服务器发出请求。此外,ID令牌不得用于向资源服务器发出请求。
ID token 与 Access Token的区别:
- ID令牌将由OAuth客户端读取,访问令牌将由资源服务器读取。
- ID令牌是JWT。访问令牌可以是JWT,但也可以是随机字符串。
- ID令牌绝不应该发送到API,客户端不应读取访问令牌。
2、Refresh Token
OAuth's Refresh Token是一个字符串,OAuth Client可以使用该字符串在无需用户交互的情况下获取新的 Access Token。
具体方法是,B网站颁发令牌的时候,一次性颁发两个令牌,一个用于获取数据,另一个用于获取新的令牌(refresh token 字段)。令牌到期前,用户使用 refresh token 发一个请求,去更新令牌。网站验证通过以后,就会颁发新的令牌。A站请求格式如下:
https://b.com/oauth/token?
grant_type=refresh_token&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET&
refresh_token=REFRESH_TOKEN
3、OAuth Scope
作用域是OAuth2.0中的一种机制,用于限制应用程序对用户帐户的访问。应用程序可以请求一个或多个作用域,然后在“同意”屏幕中将此信息显示给用户,并且向应用程序颁发的访问令牌将限于授予的作用域。
OAuth规范允许授权服务器或用户(对照应用所请求的作用域)修改授予应用程序的作用域,尽管在实践中没有很多这样做的服务示例。
OAuth并没有为作用域定义任务特定的值,因为他高度依赖于服务的内部架构与需求。
目前主流的作用域服务示例:
- github
- Slack
- FitBit
二、OAuth2 四种授权类型
1、Authorization Code
授权码方式,指的是第三方应用先申请一个授权码,然后再用该码获取令牌。
这种方式是最常用的流程,安全性也最高,它适用于那些有后端的 Web 应用。授权码通过前端传送,令牌则是储存在后端,而且所有与资源服务器的通信都在后端完成。这样的前后端分离,可以避免令牌泄漏。
授权码授权类型由机密和公共客户端用于为访问令牌交换授权码。用户通过重定向URL返回客户端后,应用程序将从URL获取授权代码,并使用它请求访问令牌。
建议所有客户端将PKCE扩展与此流一起使用,以提供更好的安全性。
具体实现过程与流程阮一峰的相关博客。
Legacy: Implicit Flow (隐藏式)
隐式流是一种简化的OAuth流。用于纯原生app与纯Web前端应用,无需与后端交互,需要将令牌存储在前端,而无需授权码这个中间环节,这种方式称之为隐藏式
-
- A站前端发送请求给B站前端
https://b.com/oauth/authorize?
response_type=token&
client_id=CLIENT_ID&
redirect_uri=CALLBACK_URL&
scope=read
- B前端直接返回
https://a.com/callback#token=ACCESS_TOKEN
这种方式把令牌直接传给前端,是很不安全的。因此,只能用于一些安全要求不高的场景,并且令牌的有效期必须非常短,通常就是会话期间(session)有效,浏览器关掉,令牌就失效了。
密码式
密码授权类型是一种将用户凭据交换为访问令牌的方法。如果你高度信任某个应用,RFC 6749
也允许用户把用户名和密码,直接告诉该应用。该应用就使用你的密码,申请令牌,这种方式称为"密码式"(password)。
-
- A 网站要求用户提供B站的用户名和密码。拿到以后,A 就直接向 B 请求令牌。
https://oauth.b.com/token?
grant_type=password&
username=USERNAME&
password=PASSWORD&
client_id=CLIENT_ID
-
- B 网站验证身份通过后,直接给出令牌。注意,这时不需要跳转,而是把令牌放在 JSON 数据里面,作为 HTTP 回应,A 因此拿到令牌。
由于客户端应用程序必须收集用户的密码并将其发送到授权服务器,因此不建议再使用此授权。而且此流程没有为多因素身份验证或委托帐户之类的事情提供任何机制,因此在实践中非常有限。
凭证式
客户端凭据授予类型由客户端用于在用户上下文之外获取访问令牌。适用于没有前端的命令行应用,即在命令行下请求令牌
-
- A 应用在命令行向 B 发出请求
https://oauth.b.com/token?
grant_type=client_credentials&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET
-
- B 网站验证通过以后,直接返回令牌。
这种方式给出的令牌,是针对第三方应用的,而不是针对用户的,即有可能多个用户共享同一个令牌。
- B 网站验证通过以后,直接返回令牌。
A 网站拿到令牌以后,就可以向 B 网站的 API 请求数据了。
此时,每个发到 API 的请求,都必须带有令牌。具体做法是在请求的头信息,加上一个Authorization字段,令牌就放在这个字段里面。
延申内容
- PKCE(Proof Key for Code Exchange): 授权码增强模式
- Device Code:主要会出现在凭证式授权类型中。设备代码,设备流中无浏览器或输入受限设备用于将事先前获得的设备代码作为获取accesstoken的交换。
Device Code授权类型的值是urn:ietf:params:oauth:granttype:device_code
. - 客户机类型:分为机密客户端与公共客户机。
机密客户机是能够安全地通过授权服务器进行身份验证的应用程序,例如能够安全地保护其注册的客户机机密。
公共客户端无法使用已注册的客户端机密,比如浏览器或移动设备上运行的应用。
参考:
https://datatracker.ietf.org/doc/html/rfc6749
https://oauth.net/
https://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html