标识服务器 4 PKCE 错误: "Transformed code verifier does not match code challenge"



我无法获得使用Postman工作的IdentityServer4 PKCE授权。

使用在线工具,我创建了必要的部件:

选择一个随机字符串:

1234567890

获取其SHA-256哈希:

c775e7b757ede630cd0a1113bd102661ab38829ca52a6422ab782862f68646

Base64对哈希进行编码以获得代码挑战:

Yzc3NWU3Yjc1N2VkZTYzMGNkMGFhMTExM2JkMTAyNjYxYWIzODgyOWNhNTJhNjQyMmFiNzgyODYyZjI2ODY0Ng==

在浏览器中,我导航到以下URL,填写凭据并从碎片重定向URL中检索代码。

GET https://localhost:5000/connect/authorize
?client_id=pkceclient
&scope=openid
&response_type=code
&redirect_uri=https://jwt.ms
&state=abc
&nonce=xyz  
&code_challenge=Yzc3NWU3Yjc1N2VkZTYzMGNkMGFhMTExM2JkMTAyNjYxYWIzODgyOWNhNTJhNjQyMmFiNzgyODYyZjI2ODY0Ng==
&code_challenge_method=S256

当用代码兑换代币时,我通过了code_verifier(SHA-256哈希(,但我的IdentityServer记录了以下错误:

"转换的代码验证器与代码挑战不匹配"。

POST https://localhost:5000/connect/token
client_id=pkceclient
grant_type=authorization_code
code:-CesrmjPYjdLdDd5AviOZpR6GdjjkZia_ZapoJdGUZI
redirect_uri=https://jwt.ms
code_verifier=c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646

在他的博客文章中,作者使用以下代码来生成零件。

var verifier = CryptoRandom.CreateRandomKeyString(64);
var challenge = verifier.ToCodeChallenge();

但是我在CCD_ 1方法的存储库中找不到代码。

为什么我手动生成的挑战与验证过程中使用的挑战不匹配,我缺少什么?

在整理这个问题时,我看到了PKCE的规范文档,发现了以下行:

code_challenge=BASE64URL-ENCODE(SHA256(ASCII(code_verifier((

原来ASCII部分不是由我使用的在线工具执行的。

在代码中实现这些步骤时,我得到了以下内容,当替换之前的值时,将在流程的第二步中通过验证。

var codeVerifier = "c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646";
var codeVerifierBytes = Encoding.ASCII.GetBytes(codeVerifier);
var hashedBytes = codeVerifierBytes.Sha256();
var transformedCodeVerifier = Base64Url.Encode(hashedBytes);

code_challenge:51FaJvQFsiNdiFWIq2EMWUKeAqD47dqU_cHzJpfHl-Q

code_verifier:c775e7b757ede630cd0a1113bd102661ab38829ca52a6422ab782862f68646

以下链接有助于实现PKCE AuthZ代码流。

https://auth0.com/docs/api-auth/tutorials/authorization-code-grant-pkce

https://github.com/gilbert-fernandes/S256Code/blob/master/src/S256Code.java

这里对所选答案进行了轻微改进,不需要Sha256()扩展方法(信用(。

code_verifier随机生成器(用于/connect/token端点(:

private string GenerateCodeVerifier()
{
var rng = RandomNumberGenerator.Create();
var bytes = new byte[32];
rng.GetBytes(bytes);
// It is recommended to use a URL-safe string as code_verifier.
// See section 4 of RFC 7636 for more details.
var code_verifier = Convert.ToBase64String(bytes)
.TrimEnd('=')
.Replace('+', '-')
.Replace('/', '_');
return code_verifier;
}

基于code_verifiercode_challenge生成器(用于/connect/authorize端点(:

private string GenerateCodeChallenge(string code_verifier)
{
var code_challenge = string.Empty;
using (var sha256 = SHA256.Create())
{
var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(code_verifier));
code_challenge = Convert.ToBase64String(challengeBytes)
.TrimEnd('=')
.Replace('+', '-')
.Replace('/', '_');
return code_challenge;
}
}

用法:

using System;
using System.Security.Cryptography;
using System.Text;
[TestMethod]
public void CodesTest()
{
string code_verifier = GenerateCodeVerifier();
Console.WriteLine("code_verifier:");
Console.WriteLine(code_verifier);
string code_challenge = GenerateCodeChallenge(code_verifier);
Console.WriteLine("code_challenge:");
Console.WriteLine(code_challenge);
}

将输出:

code_verifier:
3t1_Ve6NezEoLtj-7GKAWuXOOEUXe0z9Bd-uKoZeBnE
code_challenge:
cmcJe_eAcSGnEema7PXUEDZZOSofeaUDhKJC5P--uOY

我的文章中的附加信息

这让我困惑了一段时间,因为我认为rfc提到了base64编码。它很容易被忽略,但它实际上说的是BASE64URL-ENCODE,而不仅仅是BASE64;(

BASE64URL-ENCODE(SHA256(ASCII(code_verifier))) == code_challenge

PKCE rfc文档甚至包含一个用于进行编码的c#代码示例:

https://www.rfc-editor.org/rfc/rfc7636#appendix-

static string base64urlencode(byte [] arg)
{
string s = Convert.ToBase64String(arg); // Regular base64 encoder
s = s.Split('=')[0]; // Remove any trailing '='s
s = s.Replace('+', '-'); // 62nd char of encoding
s = s.Replace('/', '_'); // 63rd char of encoding
return s;
}

相关内容

最新更新