为了解决在API中进行身份管理的需要,您必须建立在坚实的基础上。你需要在协议和标准上建立你的API安全基础设施,并且这些标准是已经通过同行评审,并被市场采用。长期以来,缺乏这样的标准这一直是大型组织希望采用RestfulAPI的主要障碍。
OAuth 2与OIDC的诞生,解决了这一问题,他是保护API的基础。OAuth是一种“协议中的协议”或“元协议”,这意味着它提供了一种有用的其他协议(如OpenID Connect、NAPS和UMA)的起点。如果您之前了解过WS基础框架的话,这与WS-Trust被用作WS-Federation、WS-Secure等的基础的方式类似。看起来,虽然它们很复杂,但为了保护那些您的服务公开,您必须使用它们。所以我们要去深入了解这些标准,以帮助您正确部署它们。
我们选择从OAuth开始非常的重要的,因为它解决了许多API提供方最重要最需要解决的需求,包括:
•接受委托访问
•减少用户和第三方之间的密码共享(所谓的“密码模式”)
•撤销访问权
比如,如果采用“密码模式”,用户通过与第三方应用程序共享其凭据来授权访问,则撤消对该应用程序的访问的唯一方法是用户更改其密码。带来的不便之处是,所有其他和此密码相关的委托访问也被撤销。而使用OAuth,用户可以撤销访问任一特定的应用程序,而不影响其他应用继续代表用户行使其授权的访问。
接下来,我们通过例子来详细了解OAuth的原理。
API访问授权
应用程序可能需要代表用户调用API,以访问用户拥有的内容,或者应用程序自身拥有所需的内容,则需要代表自己调用API。通过下图 6-1示例的场景说明这两种情况。
6-1授权示例
在这个场景中,写作应用程序是一个专门的编辑器,帮助用户撰写和编辑文章。它调用两个API,它们都属于不同的组织。第一个是素材库,它为文章提供有效的素材。第二个是文档服务,提供文档存储服务。还有第一个移动应用程序,它调用文档服务来提供从用户的移动设备访问文档的权限。
当写作应用程序调用素材库上的API时,它会代表自己进行调用。素材内容不归用户所有,因此此访问不需要用户的同意。应用程序只需要是一个注册的客户端,有权调用素材服务的API。 但是,当应用程序调用文档服务上的API以获取用户的文档时,必须代表用户发出请求。在这种情况下,访问的内容属于用户,应用程序必须获得用户的同意才能检索用户的文档。客户端应用程序本身无权访问另一个站点上的用户数据。
移动应用程序提供对用户文档的只读访问,而不提供素材的访问。它需要用户的授权才能调用文档服务并检索用户的文档。我们在示例中引入了移动应用程序,因为我们将在下面的部分中展示OAuth2.0如何为这两个应用程序强制使用不同的权限。
OAuth 2.0
回顾上一小节的用例,用户Bob,我们在OAuth2.0中,称之为资源所有者(Resource Owner),他拥有自己的文档,并且可以授权其他应用访问他的文档资源。相应的,我们把提供文档服务的API,称之为资源服务器(Resource Server)。资源所有者Bob,可以授权写作应用来访问资源服务器,在这中间,写作应用被视为客户端Client。
通过OAuth2.0, 写作应用无需知道用户的凭证,即可代理用户去访问用户的文档资源,用户也可以随时撤销该授权。并且,在授予写作应用权限之前,需要授权服务去认证用户,并且询问用户给予Client端什么样的权限。用户可以明确的指定或限制写作应用代理访问的权限,这一权限的范围可以包含在访问令牌的作用范围Scope中,而不必要给予应用更多的权限。这里的授权服务称之为授权服务器(Authorization Server),访问令牌称之为Access Token。
OAuth的参与角色:
通过以上用例,那么OAuth2.0有四个主要参与角色:
1、资源所有者(RO):API公开的数据的访问控制决策者,通常是最终用户
2、客户端 Client:希望代表数据资源所有者RO访问的服务,通常是移动应用程序、网站等,
3、授权服务器(AS):处理认证和发放授权令牌的发行方
4、资源服务器(RS):提供服务数据,或对外提供资源访问API的服务.它对应用程序或资源所有者进行身份验证,如果应用程序将代表资源所有者访问API,则需请求资源所有者的同意。在OAuth2.0中, 授权服务器和资源服务器(API)可以由同一实体操作。
6-2 OAuth 角色
公共与非公开的客户端
OAuth2.0定义了两种客户端类型:
- 公共客户端 – 一种主要在用户侧设备上运营的程序,如(手机APP应用程序)或客户端浏览器中执行的应用程序,它不能安全地存储秘密。
- 非公开客户端 – 在受保护的服务器上运行的一种应用程序,它可以安全地存储机密,可以一种安全的机制向授权服务器进行身份验证,并存储授权服务器返回的机密。
客户端类型
OAuth 2.0基于应用程序拓扑定义了三个概要的类型:
- Web应用程序 – 在受保护的后端服务器上执行代码的机密客户端。服务器可以安全地存储客户端进行身份验证所需的任何机密以及从授权服务器接收的任何令牌。
- 基于用户代理的应用程序 – 假设是一个公共客户端,代码在用户的浏览器中执行。示例:在浏览器中运行的基于JavaScript的单页应用程序。
- 本地应用程序 – 假定为在用户的设备(例如移动应用程序或桌面应用程序)上安装和执行的公共客户端。
实际上,这些定义可能重叠,因为web应用程序可能提供包含一些JavaScript的HTML页面,而单页应用程序可能有一个小的后端。有关这方面的进一步讨论,在后续章节OIDC混合流中描述。
令牌和授权码
在OAuth中,定义了两种令牌和一个授权码,换句话说,就是具有不同用途的令牌和授权码:
- 授权码 – 返回给应用程序的一种中间的不透明一次性代码,用于获取访问令牌和可选的刷新令牌。每个授权码使用一次。
- 访问令牌:应用程序用来访问API的令牌。 它表示应用程序调用API的授权,并且有一个过期时间
- 刷新令牌:一种可选令牌,当先前的访问令牌过期时,应用程序可以使用它来请求新的访问令牌。
把访问令牌想象成一个会话,它是在您登录到网站时为您创建的。只要该会话有效,您就可以继续与网站进行交互,而无需再次登录。一旦该会话过期,您可以通过密码再次登录来获取新会话。在这种类比中,刷新令牌有点类似于密码。还有,客户端需要确保刷新令牌的安全。它应该以一种安全的方式进行凭据存储。
注意:授权服务器向客户端发出刷新令牌是中可选项。发行这样的令牌最终是一个信任决定。如果您怀疑客户保留这些信息的能力特权令牌安全,请勿发行!
OAuth的Scope(作用域):
资源所有者RO希望客户机Client能够代表他们去访问资源服务API。客户端可能要求某些权限,但用户可能只授予其中一些权限,也有可能允许其他一些没有申请的一些权限。我们在日常的生活中可能会遇到,如一个应用,向你申请访问你的QQ相册,这些客户端请求的权限通常显示与你交互的UI界面中,当然,也存在一些隐式的授权,这样的页面也有可能不会呈现给用户。
那么当你点击了授权,或者通过了隐式的授权后,授予的权限就会包含在Token中,当Client访问API的时候就会需要携带包含Scope的Token去进行请求。作用域Scope中有什么,如何使用它们,它们如何显示或不显示,等等。与作用域有关的许多内容确实在OAuth规范中没有定义,OpenID Connect定义了一些,我们后续会介绍。
OAuth如何工作
OAuth 2.0 授权框架规范定义了四种方法,应用程序通过这些方法获得调用API的授权。 每个方法都使用不同类型的凭证来表示授权。 这些证书 被称为授权授予。授权使用的类型取决于用例和应用程序的类型。四种授权类型是:
- 授权码授予 Authorization code grant
- 隐含授予 Implicit grant
- 资源所有者密码凭据授予 Resource owner password credentials grant
- 客户端凭据授予 Client credentials grant
以下各节将介绍每种方法的工作原理。
授权码 Authorization Code
授权码授权类型:应用程序通过两次向授权服务器的请求来获取访问令牌。
在第一次请求中,应用通过用户的浏览器,发起对授权服务器的重定向请求,请求通过浏览器被重定向到授权服务器上的授权端点,请求授权自己来代表用户进行的API调用。浏览器重定向后,授权服务器与用户开始交互,以对用户进行身份验证并获得用户对授权请求的同意。获得用户同意后,授权服务器生成本次授权请求的授权码,然后将此授权码包含在重定向请求中,通过浏览器将授权码返回给应用。
第二次请求发生在应用获取到授权码后,应用向授权服务器的令牌端点发送获取令牌请求,在此请求中,包含了授权码和授权码验证信息,以获取访问令牌。授权服务器向该应用颁发授权令牌(AccessToken),返回访问令牌进行响应 ,应用就可以用此令牌来调用资源服务的API。图 6-3显示了步骤的顺序。
6-3 OAuth Code Flow
- 用户向应用发起请求
- 应用向OAuth服务器发出一个请求,发送一个基本的授权范围,该请求通过浏览器进行重定向。
- OAuth授权服务器接下来将向用户发起身份验证,和征求用户的授权同意。
- 用户通过输入凭据进行身份验证,并且对请求的权限进行授权,该授权将影响令牌中包含影响作用域的信息。
- 授权服务器生成本次授权请求的授权码,然后将此授权码包含在重定向请求中,通过浏览器将授权码返回给应用。
- 应用向授权服务器的令牌端点发送获取令牌请求,在此请求中,包含了授权码,以获取访问令牌。
- 授权服务器向该应用颁发授权令牌(AccessToken),刷新令牌(可选)。
- 应用使用此令牌来调用资源服务的API。
授权码类型最初是为非公开客户端设计的。
第一个(授权)请求将用户重定向到授权服务器,以便授权服务器可以与用户交互。
第二个(授权)请求可以由应用程序的后端直接向授权服务器的令牌端点发出。这是假定非公开的客户端(应用后段)能够安全地管理认证秘密,因而应用程序后端可以向授权服务器提供秘密并进行自身认证,并自认证成功后获取访问令牌。这还意味着可以将带有访问令牌的响应传递到应用程序后端,这将使应用可以在后续进行 API调用。这种设计的另外一个优点是令牌可以通过安全的反向通道响应返回。 不过, 虽然这种方式开始只针对非公开的客户端进行了优化,但通过添加PKCE的方式,使得公共客户端也可以使用这种授权码流程。
授权码 Authorization Code+PKCE
同样参考6-3 Code Flow(授权码类型图),整个流程和上节介绍的一致。但不同的地方是,通过,代码交换证明密钥PKCE( Proof Key for Code Exchange)的使用,可以防止一些恶意进程,特别是在移动设备和公共客户端上运行的应用,截获授权码并使用它获取访问令牌。以确保请求授权码的应用程序与使用授权码获取访问令牌的应用是同一应用。
使用PKCE模式下,会和授权码的方式略有不同,首先以上流程图的第二步,应用程序创建了一个密码随机字符串,称为编码验证码。它需要具有足够的长度,以防止猜测的保护。然后应用程序从编码验证码计算一个派生值,称为编码挑战码。当应用程序在图中的步骤2中发送授权请求时,它包括编码挑战码以及用于派生它的方法。
当该流程进行到步骤6时,应用向授权服务器的令牌端点请求获取访问令牌时,需要在请求中将授权码以及编码验证码同时发送到授权服务器。授权服务器使用在授权请求中接收的转换方法对编码验证码进行转换,与第二步随授权请求发送的编码挑战码进行质询匹配。这使授权服务器能够辨别是否时恶意应用程序在试图使用被盗的授权代码。只有合法的应用程序才知道代码验证器要第6步中将需要传递的编码验证码。
PKCE规范列出了可用于从编码验证码派生代码挑战码的两种转换方法,即“plain”和“S256”。使用“plain”方法,代码挑战码和验证码是相同的,因此没有针对有可能被窃取的代码挑战码的保护。使用PKCE授权码的应用程序应该使用S256转换方法,该方法使用代码验证器的base64url编码的SHA256散列来保护它。
授权请求
下面是一个应用基于PKCE模式向授权服务请求API授权的示例。 它将被定向到授权服务器的授权端点。
GET /authorize?
response_type=code
& client_id=<client_id>
& state=<state>
& scope=<scope>
& redirect_uri=<callback uri>
& resource=<API identifier>
& code_challenge=<PKCE code_challenge>
& code_challenge_method=S256 HTTP/1.1
Host: authorizationserver.com`
表 6-1. 授权请求的参数说明
参数 | 说明 |
---|---|
response_type | 指示OAuth 2.0授权类型。“code”用于指示该流程为授权码流程。 |
client_id | 应用程序的标识符,在向授权服务器注册时分配。 |
state | 一个不可猜测的字符串,对于每个调用都是唯一的,对授权服务器来说是不透明的,客户端使用它来跟踪相应请求和响应之间的状态,以降低CSRF攻击的风险。它应该包含一个将请求与用户会话相关联的值。这可以通过将会话cookie或其他会话标识符的散列与附加的每个请求唯一组件连接起来来实现。当收到响应时,客户机应确保响应中的状态参数与从同一浏览器发送的请求的状态参数匹配。 |
scope | 本次请求授予权限的范围。例如:“read:documents” |
redirect_uri | 指示应用程序的回调URL,授权服务器将其带有授权代码的响应发送到此URL。 |
resource | 在请求访问令牌的授权服务器上注册的特定API的标识符。此参数在中定义 OAuth2.0扩展当中,资源指示符有些实现可能会使用其他名称,例如“audience.”,主要用于具有自定义API的部署中。如果具有多个API服务,用于区分,否则不需要此参数。 |
code_challenge | PKCE挑战码,由PKCE编码验证码派生 |
code_challenge_method | PKCE中指示生成挑战码的派生方法。 “S256” or “plain.” |
注:“resource”参数不在原始OAuth2.0规范中,刚开始,授权服务器被编写来处理多个API的请求。后来为了可以支持特定API的请求,额外的参数用来指示授权请求的特定API,该参数可以称为“resource”或“audience”。
授权响应
授权服务器向应用程序的回调地址发送如下响应,回调地址在授权请求的redirect_uri参数中指定。
HTTP/1.1 302 Found
Location: https://clientapplication.com/callback?
code=<authorization code>
& state=<state>
表 6-2. 授权响应的参数说明
参数 | 说明 |
---|---|
code | 应用程序用于请求访问令牌的授权码。 |
state | 在授权请求中发送的未修改的state值。应用程序必须验证响应中的状态值是否与初始请求发送的状态值匹配。 |
请求Token令牌
在接收到授权码之后,应用程序在对授权服务器的令牌端点发起的第二次请求中使用授权码来获取访问令牌。
POST /token HTTP/1.1
Host: authorizationserver.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
& code=<*authorization_code*>
& client_id=<*client id*>
& code_verifier=<*code verifier*>
& redirect_uri=<*callback URI*>
表 6-3. 请求授权令牌中的参数说明
参数 | 说明 |
---|---|
grant_type | 指示OAuth 2.0授权类型,指定为“code”用于指示该流程为授权码流程。 |
code | 授权响应中收到的授权码 |
client_id | 应用程序的标识符,在向授权服务器注册时分配。 |
code_verifier | 为了派生代码挑战码而产生的PKCE的验证码值。它应该是一个长度介于43到128个字符(含)之间的不可使用的加密随机字符串,使用字符A–Z、A–Z、0–9、“-”、“.”、“”和“~” |
redirect_uri | 授权服务器响应的回调URI。 应该与授权请求中传递给授权端点的重定向uri的值相匹配 |
来自令牌终结点的响应将示例以下内容类似:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"<*access_token_for_API*>",
"token_type":"Bearer",
"expires_in":<*token expiration*>,
"refresh_token":"<*refresh_token*>"
}
响应参数如表 6-4所示。
表 6-4. 令牌响应中的参数说明
参数 | 说明 |
---|---|
access_token | 用于调用API的访问令牌。 不同的授权服务器可能对访问令牌使用不同的格式。 |
token_type | 颁发的令牌类型。 |
expires_in | 令牌的有效期 |
refresh_token | 刷新令牌 是可选的。 是否返回刷新令牌由授权服务器自行决定。有关更多信息,请参阅本章后面的刷新令牌部分。 |
隐式授权 Implicit Grant
OAuth2.0定义了一个隐式授权类型,该类型针对公共客户机(如单页应用程序)进行了优化。使用此授予类型可在一个请求中返回对应用程序的访问令牌。它是在浏览器中不广泛支持CORS(Cross-Origin Resource Sharing,跨源资源共享)标准的时候设计的,它们只能调用加载页面的域,这意味这类单页应用它们不能调用授权服务器的令牌端点。为了弥补这一限制,隐式授权类型让授权服务器通过使用URL散列片段在重定向中向应用程序返回令牌来响应授权请求。隐式授权类型的交互如图所示 6-4.
6-4 隐式授权流程
- 用户向应用发起请求
- 应用向OAuth服务器发出一个请求,发送一个基本的授权范围,该请求通过浏览器进行重定向。
- OAuth授权服务器接下来将向用户发起身份验证,和征求用户的授权同意。
- 用户通过输入凭据进行身份验证,并且对请求的权限进行授权,该授权将影响令牌中包含影响作用域的信息。
- 授权服务器生成本次授权请求的授权令牌(AccessToken),然后将此令牌包含在重定向请求中,通过浏览器将授权令牌(AccessToken)返回给应用。
- 应用使用此令牌来调用资源服务的API。
自从OAuth2.0规范最初发布以来,CORS已经得到了大多数浏览器的支持。因此,最初为了这个目的而设计隐式授权类型就不再需要。此外,在URL散列片段中返回访问令牌会使访问令牌暴露于通过浏览器历史或referer头,存在的潜在泄漏风险。因此,对于需要访问令牌的单页应用程序,不再建议使用URL哈希片段中返回访问令牌的隐式授予类型,而应改用授权码(PKCE)授予类型。
还应该注意的是,在原始OAuth2.0规范发布之后,OAuth2.0多响应类型编码实践(Multiple Response Type Encoding Practices s)规范为授权请求定义了一个新的“Response_mode”参数,使应用程序能够请求以新的方式返回授权服务器响应。随后的规范定义了新的响应机制——Form Post Response Mode,将响应参数编码为HTML表单,该表单通过HTTP-Post发送到应用程序。OAuth 2.0 Web消息响应模式有一个规范草案,它利用HTML5Web消息传递将授权响应返回给应用程序。具有替代隐式授权类型响应的模式,为应用程序提供了新的选项,可以缓解与默认响应模式相关的问题。
授权请求
隐式授权类型的授权请求示例如下所示,其参数与前面的授权类型类似,但响应类型为“token” ,用于指示使用隐式授予类型,以及响应模式设置response_mode=form_post模式:
GET /authorize?
response_type=token
& response_mode=form_post
& client_id=<*client_id*>
& scope=<*scope*>
& redirect_uri=<*callback uri*>
& resource=<*API identifier*>
& state=<*state*> HTTP/1.1
Host: authorizationserver.com
隐式授权类型,如果使用默认的响应模式。则成功授权请求响应将访问令牌、令牌类型、令牌过期和状态值通过referer头重定向回应用程序的重定向URI,URL片段中的和浏览器历史会暴露这些敏感的信息。 使用form_post 模式的请求响应,将使这些值以HTML形式编码的响应发布到重定向uri,避免URL片段暴露。
资源所有者密码凭证模式
资源所有者密码凭据授予类型支持这样的情况:应用程序被信任来处理最终用户凭据,并且不可能有其他授予类型。对于这种授予类型,应用程序直接收集用户的凭据,而不是将用户重定向到授权服务器。应用程序将收集的凭据传递给授权服务器进行验证,作为其获取访问令牌请求的一部分。 不鼓励使用此授予类型,因为它会向应用程序公开用户的凭据。它被用于用于遗留的嵌入式登录用户迁移场景。在这两种场景下,应用程序中的漏洞都会损害凭据。此外,此授予类型不涉及用户同意步骤,因此应用程序可以使用用户的凭据请求其希望的任何访问,用户无法防止应用滥用其凭据。 因此,这种授权类型主要推荐用于用户迁移的用例。如果需要使用不兼容的密码散列将用户从一个标识存储库迁移到另一个标识存储库,则新系统可以提示用户输入其凭据,使用资源所有者密码授权来根据旧系统对其进行验证,如果有效,从旧系统检索用户配置文件,并将其和凭据存储在新系统中。这可以避免在迁移身份信息时进行大规模强制密码重置的必要性。如果使用此授予类型,则客户端应在获得访问令牌后立即丢弃用户凭据,以减少凭据受损的可能性。
资源所有者密码授予的交互类型如图6-5所示:
6-5 资源所有者密码凭证
- 用户向应用发起请求
- 应用直接向用户发起身份验证。
- 用户将输入凭据提供给应用。
- 应用将用户凭据传递给授权服务器,并请求令牌。
- 授权服务器向该应用颁发授权令牌(AccessToken),刷新令牌(可选)。
- 应用使用此令牌来调用资源服务的API。
这种授权模式过去也用于移动应用程序调用第一方API。采用如此做法,是因为在移动设备上通过浏览器重定向的登录流被认为是比较麻烦的。但是,这一点在RFC8252规范中已经得到了改进。现在建议OAuth2.0(适用于本机应用程序)使用授权码授权 PKCE的模式来进行授权。
授权请求
资源所有者密码授予类型的令牌请求示例如下所示, 参数与之前的请求类型相似,但授予类型使用“grant_type=password”的类型,以及包含从用户处收集的用户名和密码。应用程序通过获得的用户凭据作为应用程序的授权, 并用于从令牌端点请求访问令牌。
POST /token HTTP/1.1
Host: authorizationserver.com
Authorization: Basic <encoded application credentials>
Content-Type: application/x-www-form-urlencoded
grant_type=password
& scope=<*scope*>
& resource=<*API identifier*>
& username=<*username*>
& password=<password>
此示例需要应用程序使用基本身份验证方案,通过HTTP的方式对授权服务器进行身份验证。应用请求中包含从授权服务器获得的客户机ID和密码。成功的请求将获得来自令牌端点响应的访问令牌以及可选的刷新令牌。
客户端凭据模式
当应用程序调用API以访问应用程序拥有的资源时,将使用客户端凭据授予类型。示例如图6-1所示素材服务,素材服务不属于单个用户,因此应用程序可以去直接进行调用。应用程序使用客户端凭据授予类型,并使用自己的凭据对授权服务器进行身份验证,以获取访问令牌。该类型的顺序图如图所示 6-6所示。
6-6 客户端凭据流程
- 应用程序将包括应用程序凭据的授权请求发送到授权服务器。
- 授权服务器验证凭据并使用访问令牌进行响应。
- 应用程序使用此令牌来调用资源服务的API。
此流不需要最终用户与授权服务器交互。应用只需要通过自己的凭据去获取授权,该凭据由应用程序向授权服务器注册时获得,包含客户机Client_ID和Client_Secret。应用使用该凭据从令牌端点请求访问令牌。
授权请求
下面是客户端凭据授予类型的示例令牌请求,其参数定义与之前授予类型的类似, 但grant_type设置为“client_credentials”。在本例中,应用程序使用在授权服务器上注册的客户端Cilent_ID和Secret进行身份验证。
POST /token HTTP/1.1
Host: authorizationserver.com
Authorization: Basic <encoded application credentials>
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
& scope=<*scope*>
& resource=<*API identifier*>
成功的请求将获得来自令牌端点响应的访问令牌。
API调用
一旦应用程序接收到访问令牌,它就会调用资源服务器并传递访问令牌。虽然不同的API的调用会有差异,但典型的方法是使用HTTP“Authorization”请求头字段,在授权头中使用承载身份验证令牌类型,并使用访问令牌,如以下代码段所示:
GET /api-endpoint HTTP/1.1
Host: api-server.com
Authorization: Bearer <*access_token*>
访问令牌具有时效性,只能在有限的时间内使用,但它不是一次性使用令牌。为了避免对每个API调用都调用授权服务器,考虑了性能优化方面,应用程序可以缓存访问令牌并在其过期之前重用它。同时,必须为访问令牌授予适当的权限范围,以用于限制API调用。不鼓励使用过宽的范围!
刷新令牌
OAuth 2.0中访问令牌具有有效期,当访问令牌过期时,应用程序可以发出新的授权请求,但是OAuth2.0为传统web应用程序和本机客户端定义了一种替代方法,该方法涉及刷新令牌。应用可以从授权服务器获取刷新令牌,并在先前的访问令牌过期时用于获取新的访问令牌。例如,刷新令牌可用于支持移动应用程序的持续API访问。
刷新令牌并非在所有场景中都适用。首先,客户端凭据模式不适用,由于应用客户端可以随时请求访问令牌,而不需要用户交互,因此不需要授予客户端凭据的刷新令牌。另外,静态刷新令牌不适用于公共客户端,因为它们是敏感令牌,公共客户端无法安全地存储它们。因此,OAuth2.0威胁模型和安全注意事项文档提出了刷新令牌轮换的概念,以检测刷新令牌是否被盗,以及是否被两个或多个客户端使用。此方案使授权服务器在每次访问令牌续订请求时返回一个新的刷新令牌。 OAuth 2.0安全性最佳当前实践文档规定,授权服务器必须对公共客户端使用刷新令牌轮换或者具有发送方约束的刷新令牌(绑定到特定客户端),以降低刷新令牌被窃的风险。
刷新令牌为传统Web应用程序和本机应用程序获取新的访问令牌提供了一种方便的方法。 这有助于使用持续时间较短的访问令牌,从而将访问令牌受损的风险降至最低。一旦访问令牌过期,就自动刷新它可能很有诱惑力,但是为了保持最小特权的原则,最好只在需要时刷新访问令牌,而不是总是将当前的访问令牌保留在手边。 同样,应用程序必须安全地存储刷新令牌,因为它是敏感凭据。
OAuth2.0规范没有为应用程序提供请求刷新令牌的机制,这让授权服务器自行决定是否发布令牌。因此,刷新令牌的处理可能会因各个授权服务器而异。有些自动发出刷新令牌,有些则希望应用程序显式请求刷新令牌(下一章将介绍的OIDC规范包括一种机制,用于应用程序为一个特定的用例请求刷新令牌。)撤销访问令牌的能力不是OAuth2.0规范中的强制功能,因此一些授权服务器可能不支持它。您选择的授权服务器的文档应解释具体实现的详细信息。
下面的示例中显示了对授权服务器的令牌端点的示例调用,以请求新的访问令牌。客户端必须对请求进行身份验证。
POST /token HTTP/1.1
Host: authorizationserver.com
Authorization: Basic <encoded application credentials>
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
& refresh_token=<*refresh_token*>
访问令牌将以类似于前面部分中描述的响应的方式返回。scope参数是可选的,如果使用,则必须等于或小于原始授权请求中的作用域,并且传递给授权服务器的客户端凭据必须是发出原始授权请求的应用程序的客户端凭据。
指导原则
前面介绍了应用程序如何通过OAuth2.0请求API授权。当然,关于OAuth2.0已经有些抽象的SDK,可以简化某些交互,或者使用不同的参数名。这时,您需要查看授权服务器的文档,以了解特定于实现的详细信息。然而,即使您使用SDK,了解底层调用的形式,也有利于故障排除。另外,一定要检查OAuth2.0规范,因为有几个额外的请求参数可用于更高级的用例。
访问令牌意味着由API去消费。 访问令牌的格式可能不同,但应用程序不应依赖于使用访问令牌中的数据(在没有专有扩展的情况下)。接收访问令牌的API必须在处理其伴随的请求之前对其进行验证,验证令牌的过程可能因授权服务器实现而异。
一般来说,建议访问令牌持续时间短,如果以前的访问令牌已过期,则在需要时获取新的访问令牌。确切的持续时间应根据访问资源的敏感性来确定。可以将访问令牌缓存一段小于或等于其过期时间的时间,以优化性能和/或避免对授权服务器进行过多调用而达到速率限制。需要注意的是,访问令牌和刷新令牌必须安全存储,因为它们是敏感凭据。在存储这些令牌时,应该利用平台的安全存储选项。
本章重点回顾
OAuth2.0协议使应用程序能够获得代表用户或自己调用API的授权。这样就不需要用户与应用程序共享其凭据。它还为用户提供了对应用程序可以执行的操作的更大的控制,并限制了API访问的持续时间。用户可以撤销单个应用程序的API访问,而不影响其他应用程序代表其调用API的能力。一旦您有一个应用程序被授权调用一个API,您就需要对该应用程序的用户进行身份验证,这将在下一章中介绍。
一些需要注意的点:
- 作用域用于控制应用程序在调用API时的访问权限。
- PKCE的授权码模式可以用于传统的Web应用程序、公共应用程序以及本机应用程序的授权使用。
- 不建议使用OAuth2.0隐式授予类型获取具有默认响应模式的访问令牌,因为它会使访问令牌暴露于潜在的危害。需要使用form_post方式。
- OAuth2.0资源所有者密码授予类型最好仅限于旧式用户迁移情况,因为它向应用程序公开用户凭据。
- 客户端凭据授予类型用于应用程序拥有请求的资源的API调用。
- 刷新令牌用于在旧的访问令牌过期时获取新的访问令牌。OAuth2.0威胁模型和安全注意事项文档提出了刷新令牌轮换的概念,以检测刷新令牌是否被盗。要求授权服务器在每次访问令牌续订请求时返回一个新的刷新令牌。