使用 PKCE 登录 Discord OAuth 的过程

Posted

技术标签:

【中文标题】使用 PKCE 登录 Discord OAuth 的过程【英文标题】:Procedure for logging into Discord OAuth using PKCE 【发布时间】:2020-05-31 09:11:40 【问题描述】:

我正在尝试使用 PKCE 将 Discord OAuth 添加到移动应用程序。我能够成功通过 DiscordAPI 进行身份验证,并且我收到了来自服务器的 code

但是,当我尝试调用 https://discordapp.com/api/oauth2/token 端点时,我不断得到 Bad Request 结果。

我尝试了几种方法来获得我的令牌,但都失败了。

尝试(1)使用HttpClient:

var nvc = new List<KeyValuePair<string, string>>();
nvc.Add(new KeyValuePair<string, string>("client_id", Constants.ClientId));
nvc.Add(new KeyValuePair<string, string>("client_secret", Constants.Secret));
nvc.Add(new KeyValuePair<string, string>("code_verifier", codeVerifier)); // method param
nvc.Add(new KeyValuePair<string, string>("grant_type", "authorization_code"));
nvc.Add(new KeyValuePair<string, string>("code", authCode)); // method param
nvc.Add(new KeyValuePair<string, string>("redirect_uri", Constants.Redirect));
nvc.Add(new KeyValuePair<string, string>("scope", "identify email connections"));

var req = new HttpRequestMessage(HttpMethod.Post, "https://discordapp.com/api/oauth2/token") 
 
    Content = new FormUrlEncodedContent(nvc) 
;
var authTokenResponse = await this.Client.SendAsync(req).ConfigureAwait(false);
// results in BadRequest

我也尝试过像这样使用StringContent 对象:

var stringContent = new StringContent($"grant_type=authorization_code&client_id=Constants.ClientId&client_secret=Constants.Secret&code_verifier=codeVerifier&code=authCode&redirect_uri=Constants.Redirect&scopes=identify%20email%20connections", Encoding.UTF8, "application/x-www-form-urlencoded");

这两个结果都是Bad Request

尝试(2)使用 RestSharp

var client = new RestSharp.RestClient($"this.Client.BaseAddress/oauth2/token");
var request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddParameter("application/x-www-form-urlencoded", $"grant_type=authorization_code&client_id=Constants.ClientId&client_secret=Constants.Secret&code_verifier=code&code=authCode&redirect_uri=Constants.Redirect&scopes=identify%20email%20connections", ParameterType.RequestBody);
var response = await client.ExecuteAsync(request);

我已经仔细检查了我的所有值,并确保我的客户 ID 和秘密是正确的。我尝试发送带有和不带有code_verifier 的请求。没有code_verifier 我得到Unauthorized

问题

我应该如何修改我的 HttpClient 请求以成功获得我的身份验证令牌?

更多信息:

为了符合 PKCE,我需要一个code_verifier。这就是我生成代码的方式。 (注意,我试图将其用作身份验证代码。)

Random rng = new Random();
byte[] code = new byte[32];
rng.NextBytes(code);

CodeVerifier = Base64UrlEncoder.Encode(rng.ToString());

然后我使用从c-sharpcorner.com借来的哈希算法,看起来像这样:

using (SHA256 sha256Hash = SHA256.Create())

    // ComputeHash - returns byte array  
    var bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(rawData));

    // Convert byte array to a string   
    var builder = new StringBuilder();
    for (int i = 0; i < bytes.Length; i++)
    
        builder.Append(bytes[i].ToString("x2"));
    
    return builder.ToString();

我将结果作为我的code_challenge 传递,同时获得我的身份验证码。

这是我了解到需要 PKCE 的地方。

https://github.com/discordapp/discord-api-docs/issues/450

PKCE 讨论在此处继续:

https://github.com/discordapp/discord-api-docs/issues/1296

更新:

好吧,所以基于iwokal's 的回答,我重新阅读了上面链接的帖子,所以只有在使用Custom Scheme Redirect 时才“需要”PKCE,如果你只是使用http 重定向,只需离开code_verifier 出去,你就可以走了。

也就是说,这个 PKCE 工作流程没有记录。由于我花费了 500 积分,因此我会将积分奖励给能够解决 PKCE OAuth 工作流程的人。

您可以从头开始设计一个解决方案,我会对其进行测试。

【问题讨论】:

如果您需要更多详细信息,请告诉我。 代码优先用于保护客户端,客户端向提供者请求代码,客户端将代码提供给您的服务器,您的服务器请求令牌。 【参考方案1】:

好吧,经过一些研究,我可能知道发生了什么。您是否尝试通过您创建的授权码获取令牌?

我相信正确的方法是发送https://discordapp.com/api/oauth2/authorize 请求,该请求将重定向到提供的带有包含正确身份验证代码的查询字符串的url。

您应该在获取令牌的步骤中使用此代码。

我不知道这是否会改变任何东西,但我相信根据文档没有“范围”之类的参数,它应该是“范围”。

【讨论】:

感谢 iwokal 的提示,我尝试使用 "scope" 而不是 "scopes",但仍然看到 400“错误请求”。 @DanBeaulieu 我不确定我是否理解正确,但是看到带有代码验证器和散列算法的部分让我假设您是自己创建身份验证代码,而不是使用从 Discord Api 返回的代码。为了在此过程中为您提供帮助,我发现了这两个主题:***.com/questions/43260556/discord-oauth-code-usage 和 yogihosting.com/discord-api-asp-net 不,我已经多次连接 OAuth,我确实了解工作流程。我不想创建自己的身份验证代码。这是我第一次不得不使用 PKCE(在 OP 中链接),这是服务器告诉每个请求它与同一个客户端对话的一种方式。我在调试中阅读了这两个链接,它们已经过时了。我会找到描述 PKCE 是必要的文章并将其链接到我的 OP。 添加到 OP 的链接,其中 PKCE 被描述为 OAuth 的需要 你可能已经把我带到了正确的道路上,它说“自定义方案重定向”需要 PKCE,也许如果我切换到标准的 http:// 重定向,这将起作用。跨度> 【参考方案2】:

您向错误的网址发出请求。创建代码流是为了防止泄漏客户端令牌。流程如下

    客户端向 discord 服务器请求代码 客户端将该代码发送到您的服务器 您的服务器将代码及其机密传递给不和谐服务器 Discord 服务器响应配置的 redirect_uri,获取令牌。

注意:有 2 个重定向 uri,1 个用于解析代码,另一个用于解析令牌。

您正在尝试直接向令牌端点发出请求。您需要向 Authorize 端点发出请求。获取令牌

https://discordapp.com/api/oauth2/authorize?response_type=code&client_id=157730590492196864&scope=identify%20guilds.join&state=15773059ghq9183habn&redirect_uri=https%3A%2F%2Fnicememe.website&prompt=consent

var state = Guid.NewGuid().ToString();
var codeParams = new List<KeyValuePair<string, string>>();
codeParams.Add(new KeyValuePair<string, string>("client_id", Constants.ClientId));
codeParams.Add(new KeyValuePair<string, string>("redirect_uri", Constants.Redirect));
codeParams.Add(new KeyValuePair<string, string>("scope", "identify email connections"));
codeParams.Add(new KeyValuePair<string, string>("response_type", "code"));
codeParams.Add(new KeyValuePair<string, string>("state", state))

var req = new HttpRequestMessage(HttpMethod.Post, 
"https://discordapp.com/api/oauth2/authorize") 
 
    Content = new FormUrlEncodedContent(nvc) 
;
var authTokenResponse = await this.Client.SendAsync(req).ConfigureAwait(false);

Discord 将回复您的 redirect_uri 代码和状态

https://mywebsite.com/?code=NhhvTDYsFcdgNLnnLijcl7Ku7bEEeee&state=15773059ghq9183habn

注意:对 Authorize 的原始请求不包含您的客户端密码,客户端密码仅用于您的服务器。这样只有服务器可以请求 AuthTokens 注意:大多数支持 oauth 的地方都要求您设置您的服务器重定向 url。 注意:状态也将返回,代码验证您发送的状态是返回的状态。

获取返回给您的代码 (NhhvTDYsFcdgNLnnLijcl7Ku7bEEeee),并使用您的原始示例请求令牌,并使用正确的代码:

var nvc = new List<KeyValuePair<string, string>>();
nvc.Add(new KeyValuePair<string, string>("client_id", Constants.ClientId));
nvc.Add(new KeyValuePair<string, string>("client_secret", Constants.Secret));
nvc.Add(new KeyValuePair<string, string>("code_verifier", codeVerifier)); // method param
nvc.Add(new KeyValuePair<string, string>("grant_type", "authorization_code"));
nvc.Add(new KeyValuePair<string, string>("code", authCode)); // method param
nvc.Add(new KeyValuePair<string, string>("redirect_uri", Constants.Redirect));
nvc.Add(new KeyValuePair<string, string>("scope", "identify email connections"));

var req = new HttpRequestMessage(HttpMethod.Post,
"https://discordapp.com/api/oauth2/token") 
 
    Content = new FormUrlEncodedContent(nvc) 
;
var authTokenResponse = await this.Client.SendAsync(req).ConfigureAwait(false);

请注意,当您向 Token 端点发出请求时,请确保您使用的是

'Content-Type': 'application/x-www-form-urlencoded'

如果您仍然在流程中遇到问题,请使用 Postman,它内置了 OAuth 支持并获取令牌,观察他们使用 fiddler 发送的内容,并对其进行仿真。

【讨论】:

谢谢 johnny number 5,今晚我会看这篇文章。 @DanBeaulieu 好的,如果您有任何困惑,请告诉我。当我实施 OAuth 时,您的位置与您相同,但是一旦我理解了为什么事情都完成了,一切都点击了,这就是我首先解释流程的原因。

以上是关于使用 PKCE 登录 Discord OAuth 的过程的主要内容,如果未能解决你的问题,请参考以下文章

在带有 PKCE 的 OAuth 授权流中使用时如何在 Azure 应用注册中启用 CORS?

如何使用 PKCE 为 React 单页应用程序实现 OAuth2 授权代码授予?

在我的网站上设置 Discord oauth2 登录(使用 PHP?)

关于OAuth2.0 Authorization Code+PKCE flow在原生客户端(Native App)下集成的思考

无法在 Electron 应用程序中使用 Discord OAuth2

使用 oauth2 从 Discord API 获取信息