6

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

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

选择一个随机字符串:

1234567890

获取其 SHA-256 哈希:

c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646

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();

但我在该方法的存储库中找不到代码ToCodeChallenge

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

4

3 回答 3

15

在将这个问题放在一起时,我遇到了 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);

代码挑战:51FaJvQFsiNdiFWIq2EMWUKeAqD47dqU_cHzJpfHl-Q

code_verifier: c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646

于 2019-11-04T04:21:32.977 回答
1

以下链接有助于实现 PKCE-AuthZ-Code 流程。

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

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

于 2020-03-05T18:23:12.743 回答
1

这是所选答案的轻微改进,无需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_challenge基于 a 的生成器code_verifier(用于/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

我的文章中的附加信息

于 2020-08-22T20:07:58.220 回答