4. 协议 (Protocol)
4.1 客户端创建代码验证器 (Client Creates a Code Verifier)
客户端首先为每个 OAuth 2.0 [RFC6749] 授权请求创建一个代码验证器 "code_verifier", 方法如下:
code_verifier = high-entropy cryptographic random STRING using the
unreserved characters [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~"
from Section 2.3 of [RFC3986], with a minimum length of 43 characters
and a maximum length of 128 characters.
"code_verifier" 的 ABNF 如下.
code-verifier = 43*128unreserved
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
ALPHA = %x41-5A / %x61-7A
DIGIT = %x30-39
注意: 代码验证器应当具有足够熵, 使猜测其值在实践中不可行. 推荐使用合适随机数生成器的输出创建一个 32 八位字节序列. 然后对该八位字节序列进行 base64url 编码, 生成一个 43 八位字节的 URL 安全字符串, 用作代码验证器.
4.2 客户端创建代码挑战 (Client Creates the Code Challenge)
随后, 客户端使用以下转换之一, 从代码验证器派生出代码挑战:
plain
code_challenge = code_verifier
S256
code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
如果客户端能够使用 "S256", 则必须使用 "S256", 因为服务器端必须实现 (Mandatory To Implement, MTI) "S256". 只有在客户端由于某种技术原因无法支持 "S256", 并且通过带外配置知道服务器支持 "plain" 时, 才允许使用 "plain".
plain 转换用于兼容现有部署, 以及无法使用 S256 转换的受限环境.
"code_challenge" 的 ABNF 如下.
code-challenge = 43*128unreserved
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
ALPHA = %x41-5A / %x61-7A
DIGIT = %x30-39
4.3 客户端随授权请求发送代码挑战 (Client Sends the Code Challenge with the Authorization Request)
客户端使用以下附加参数, 将代码挑战作为 OAuth 2.0 授权请求 ([RFC6749] 第 4.1.1 节) 的一部分发送:
code_challenge : 必需. 代码挑战.
code_challenge_method : 可选. 如果请求中不存在, 默认为 "plain". 代码验证器转换方法为 "S256" 或 "plain".
4.4 服务器返回代码 (Server Returns the Code)
当服务器在授权响应中签发授权代码时, 它必须将 "code_challenge" 和 "code_challenge_method" 值与该授权代码关联起来, 以便后续验证.
通常, "code_challenge" 和 "code_challenge_method" 值以加密形式存储在 "code" 本身中, 但也可以存储在服务器端并与该代码关联. 服务器禁止以其他实体可提取的形式在客户端请求中包含 "code_challenge" 值.
服务器用于将 "code_challenge" 与已签发 "code" 关联起来的确切方法不属于本规范范围.
4.4.1 错误响应 (Error Response)
如果服务器要求 OAuth 公共客户端使用代码交换证明密钥 (Proof Key for Code Exchange, PKCE), 而客户端未在请求中发送 "code_challenge", 则授权端点必须返回授权错误响应, 其中 "error" 值设置为 "invalid_request". "error_description" 或 "error_uri" 的响应应当说明错误性质, 例如需要 code challenge.
如果支持 PKCE 的服务器不支持所请求的转换, 则授权端点必须返回授权错误响应, 其中 "error" 值设置为 "invalid_request". "error_description" 或 "error_uri" 的响应应当说明错误性质, 例如不支持转换算法.
4.5 客户端向令牌端点发送授权代码和代码验证器 (Client Sends the Authorization Code and the Code Verifier to the Token Endpoint)
收到授权代码后, 客户端向令牌端点发送访问令牌请求. 除 OAuth 2.0 访问令牌请求 ([RFC6749] 第 4.1.3 节) 中定义的参数外, 它还发送以下参数:
code_verifier : 必需. 代码验证器.
"code_challenge_method" 在签发授权代码时绑定到该授权代码. 这就是令牌端点必须用于验证 "code_verifier" 的方法.
4.6 服务器在返回令牌前验证 code_verifier (Server Verifies code_verifier before Returning the Tokens)
令牌端点收到请求后, 服务器先根据客户端指定的 "code_challenge_method" 方法对收到的 "code_verifier" 进行转换, 计算出代码挑战, 再将其与先前关联的 "code_challenge" 进行比较, 从而完成验证.
如果第 4.3 节中的 "code_challenge_method" 为 "S256", 则对收到的 "code_verifier" 进行 SHA-256 哈希, 再进行 base64url 编码, 然后与 "code_challenge" 比较, 即:
BASE64URL-ENCODE(SHA256(ASCII(code_verifier))) == code_challenge
如果第 4.3 节中的 "code_challenge_method" 为 "plain", 则直接比较二者, 即:
code_verifier == code_challenge
如果值相等, 令牌端点必须按正常流程继续处理 (如 OAuth 2.0 [RFC6749] 所定义). 如果值不相等, 必须返回 [RFC6749] 第 5.2 节所述的错误响应, 指示 "invalid_grant".