07. 问渠哪得清如许,为有源头活水来:授权码模式之授权请求和令牌请求

在上一小节我向你介绍了OAuth 2中最重要也是最安全的授权模式——授权码模式,同时我向你介绍了要完成整个流程的交互,客户端至少应该发起两个请求——授权请求和令牌请求,这一小节我向你详细介绍这两个请求的详细内容。掌握这两个请求的必要性在于这是我们实现授权服务器的基础,如果你要实现自己的授权服务器,请务必遵循规范规约的这些请求和响应内容。

授权请求

参数说明

  1. client_id:必须参数。客户端的唯一标识符,是客户端向授权服务器表明身份的参数。我在第(4)小节中向你说明了如何获得该凭证。
  2. response_type:必须参数。在授权码模式下该值必须设置为code ,表明你使用授权码模式,且你想要在响应中获得一个授权码(authorization code)。在后面你会看到密码模式和客户端模式这两种模式不需要授权码这个中间介质的参与,因此没有授权请求只有令牌请求,这就是你在这两种模式下看不到response_type参数的原因。
  3. redirect_uri:可选参数,具体取决于授权服务器的实现和规约。如果客户端注册时一个客户端只允许配置一个回调地址,那么默认客户端和授权服务器都使用该回调地址,此时该参数可选。如果客户端注册时允许配置大于一个的回调地址,此时客户端必须向授权服务器显示指明该参数,不然授权服务器无法知晓具体重定向到哪一个具体的URI。该参数表示用户在授权完成后重定向到的URI。该参数值必须与OAuth 2.0客户端注册时提供的授权回调URI完全匹配。如果该值与提供的client_id对应的授权重定向URI不匹配,则会得到一个redirect_uri_mismatch的错误。
  4. scope:可选参数。表示请求授权的访问权限范围,一般是以空格分隔、区分大小写的字符串列表,例如“profile:read profile:write”。该参数具体需要参阅授权服务方提供的用户文档。我已经在第(5)小节向你介绍了这个参数的具体含义。
  5. state:推荐参数。用于客户端维护授权请求和授权服务器响应之间状态的任意字符串。在授权请求中state参数携带的任何值,在用户完成授权决策后,授权服务器将原封不动 的在授权响应中将state参数的值返回给客户端。该参数有几个意义,例如可以标记客户端在对终端用户引导授权前终端用户在客户端的资源位置,例如终端用户在/user/me页面,在整个授权流程结束后客户端借助state参数可以将终端用户重定向至/user/me页面。该参数的另一个意义是客户端可以构造一个与客户端用户会话相关的、不可猜测的不透明字符串作为state参数值传递给授权服务器,在授权服务器响应授权码时会原封不动的返回该state参数值,客户端可以校验state参数值进而避免跨站请求伪造攻击。我们将在第三关的第(23)小节看到state如何防止CSRF攻击的。

请求示例

下面的代码段展示了授权码模式下授权请求的示例。
GET /authorize? 

response_type=code
& client_id=[CLIENT_ID]
& state=[STATE]
& scope=[SCOPE]
& redirect_uri=[REDIRECT_URI] HTTP/1.1
 Host: authorizationserver.com 

  

授权响应

成功响应

成功响应参数

在终端用户同意授权请求后,授权服务器向客户端的授权回调地址发送如下响应。该回调地址即你在上一步授权请求中指定的redirect_uri参数值。
  1. code:授权服务器授权端点颁发给客户端的授权码,客户端可以用它来进一步请求令牌
  2. state:你在上一步授权授权请求中构造的state参数值,授权服务器会在响应中原封不动的将该参数值返回给客户端

成功响应示例

HTTP/1.1 302 Found

Location: [REDIRECT_URI]?
code=[AUTHORIZATION CODE]
&state=[STATE]

错误响应

错误响应参数

如果用户拒绝授权或在授权请求的过程中发生其它异常,比如客户端不存在或者授权回调地址不匹配等,客户端的授权回调地址将会收到如下的错误响应(这是OAuth 2.0文档规范的内容,各个授权服务器厂商应该遵循该规范)。
  1. error参数是必须的,用来辅助开发者来定位问题,error通常是以下错误码之一:
    1. invalid_request:非法的授权请求。授权请求中可能缺失必要的参数或者提供的参数值异常,或者重复提供一个参数或不可辨识的授权请求。
    2. unauthorized_client:授权服务器无法认证该客户端。
    3. access_denied:终端用户拒绝了来自客户端的授权请求。
    4. unsupported_response_type:非法的授权类型(response_type)。在授权码模式下该值必须设置为code,否则授权服务器认为该授权类型非法。
    5. invalid_scope:请求的作用域无效、未知或格式不正确。
    6. server_error:授权服务器内部错误。
    7. temporarily_unavailable:授权服务器暂时不可用。
  2. error_description:(可选)描述错误原因的可读消息,用于帮助客户端开发人员了解发生的错误。
  3. error_uri:(可选)有关描述错误详细信息的网页地址。
  4. state:参数是必须的:如果请求中存在state参数,那么即使授权请求失败,授权服务器的授权端点也会在响应中返回该参数。

错误响应示例

HTTP/1.1 302 Found

Location: [REDIRECT_URI]?
error=[ERROR]
&error_description=[ERROR_DESCRIPTION]
&error_uri=[ERROR_URI]
&state=[STATE]

令牌请求

令牌是客户端代表用户获取资源的最终凭证。在用户允许授权后授权服务器将通过重定向的方式颁发给客户端一个临时的授权码,该授权码对终端用户是可见的。在上一小节我与你讨论了授权码存在的必要性。客户端在获取该授权码后将在后端服务器发起令牌请求,向授权服务器申请最终能用于向资源服务器申请终端用户资源的访问令牌。这次交互直接发生在客户端和授权服务器之间,不需要终端用户的参与,所以它是安全的。

请求参数

参数说明

令牌请求将包含以下参数
  1. grant_type:必须参数。在授权码模式下该值必须设置为authorization_code
  2. code:必须参数。在第(6)小节客户端引导用户执行授权请求并获得用户授权许可后获得的授权码。
  3. redirect_uri:可选参数。如果客户端在初始化授权请求时添加了redirect uri,那么它也必须向令牌请求添加redirect_uri且也必须与授权请求中使用的redirect_uri参数的值相同。
  4. 客户端认证信息:必须参数。在授权码模式的令牌请求中必须携带客户端认证相关的信息。一般的都使用RFC 2617定义的HTTP BASIC身份认证的方式,这种技术需要客户端发送带有“Authorization”头信息的HTTP请求,Authorization头信息由三部分组成——固定单词“Basic”,后面紧跟一个空格,最后一部分是对client_id、冒号(英文)、client_secret组合而成的字符串(即client_id:client_secret)进行Base64编码后的结果,下面是一个Authorization头信息的示例:
    Authorization Basic MTIzNDU6SGlzSyQyMTFGbWNj
另外一种方式是直接在请求中传递client_id和client_secret,尽管上面提到的HTTP BASIC身份认证方式很简单,但是一般认为它比这种方式要更安全,所以更推荐前者,但这具体取决于授权服务器的实现。当然授权服务器可以支持更强大和安全的客户端身份认证模式,我将在授权服务器实现的章节向你展开讨论这一细节。
  1. client_id:必须参数。客户端的唯一标识符,是客户端向授权服务器表明身份的参数。值得注意的是如果我们使用HTTP BASIC身份认证的方式对客户端进行身份认证,在Authorization头里已经包含了client_id的信息,

请求示例

为了使用授权码交换最终可用于访问用户资源的访问令牌,我们必须向授权服务器的令牌端点发出POST请求,传递一组特定的参数。参数必须使用application/x-www-form-urlencoded格式进行编码。下面的代码段展示了一个示例请求。
POST /oauth/token HTTP/1.1
Host: authorizationserver.com
Authorization: Basic [encoded CLIENT_ID CLIENT_SECRET] 
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=[CODE]
&redirect_uri=[REDIRECT_URI]
&client_id=[CLIENT_ID]

成功响应

成功响应参数

如果我们正确地执行了授权请求并且在授权请求中传递的参数都是正确的,那么我们将得到成功的响应,该响应包含我们期望的访问令牌(Access Token)及其它一些参数。
  1. access_token:必须参数。客户端最终代表终端用户访问终端用户资源的“钥匙”。
  2. token_type:必须参数。授权服务器颁发的令牌的类型。一般该值是“bearer”。如果你对Bearer Token感兴趣,你可以阅读RFC 6750文档(https://datatracker.ietf.org/doc/html/rfc6750)
  3. expires_in:可选参数。以秒为单位的访问令牌的生存期。该参数为非必需参数,如果缺省这个参数,授权服务器必需以其他方式告知客户端令牌的剩余生命周期。
  4. refresh_token:可选参数。刷新令牌是可选的。客户端可以用刷新令牌续期交换令牌。

成功响应示例

HTTP/1.1 200 OK
Content-Type: application/json;
charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
    "access_token":"[ACCESS_TOKEN]",
    "token_type":"Bearer",
    "expires_in":[TOKEN EXPIRED SECONDS],
    "refresh_token":"[REFRESH_TOKEN]"
}

错误响应

错误响应参数

如果令牌请求因任何原因失败,服务器将返回HTTP 400(错误请求)的状态码,并包含以下参数:
  1. error:error参数是必须的,代表令牌请求错误的原因。用来辅助开发者来定位问题,error通常是以下错误码之一:
    1. invalid_request:令牌请求缺少必要的参数,包含不支持的参数值、重复参数、包含多个凭据、使用多种机制对客户端进行身份验证,或者格式不正确。
    2. invalid_client:由于某些原因客户端身份认证失败(例如,未知客户端、没有客户端身份认证信息或不支持的身份认证方法)。
    3. invalid_grant:提供的授权类型或刷新令牌无效、过期、已吊销等,或者与授权请求中使用的重定向URI不匹配。
    4. unauthorized_client:经过身份验证的客户端无权使用这种授权类型。
    5. unsupported_grant_type:授权服务器不支持这种授权类型。
    6. invalid_scope:请求的作用域无效、未知、格式不正确或超出了资源所有者授予的作用域。
  2. error_description:(可选)描述错误原因的可读消息,用于帮助客户端开发人员了解发生的错误。
  3. error_uri:(可选)有关描述错误详细信息的网页地址。

错误响应示例

错误响应示例如下:
  HTTP/1.1 400 Bad Request    
  Content-Type: application/json;charset=UTF-8    
  Cache-Control: no-store 
  Pragma: no-cache    
  
{       
    "error":"invalid_client",
    "error_description":"[ERROR_DESCRIPTION]",
    "error_uri":"[ERROR_URI]",
}

总结

授权码模式是最广为应用的授权模式。对于授权码模式,客户端需要发起两次请求——授权请求和令牌请求,而授权服务器需要处理和响应这两个请求。OAuth 2.0规范已经定义了这两个请求具体的请求内容和响应内容,各个授权服务供应商应该遵循规约。我们在第二关实现的授权服务器响应给客户端的内容同样应该严格遵循我今天描述的内容。
 
 
posted @ 2023-05-07 11:26  小米粥|  阅读(100)  评论(0)    收藏  举报