【问题标题】:Could not determine client ID from request error while refreshing token刷新令牌时无法从请求错误中确定客户端 ID
【发布时间】:2021-08-03 16:42:52
【问题描述】:

我正在为我的 Google Oauth 设置使用已安装的应用程序客户端 ID。使用 GoogleWebAuthorizationBroker.AuthorizeAsync 成功进行用户身份验证后,我收到了来自 Google 的访问令牌,并且能够按预期使用 Google API。大约 1 小时后,如果我需要访问 Google API,我正在使用 [credential.RefreshTokenAsync] 函数来刷新访问令牌。即使使用刷新令牌,我也能够按预期访问 Google API。

但在随机情况下,我在刷新令牌时收到以下异常。我可以确认客户端 ID 没有问题,因为我的客户端 ID 是从永久文本文件中读取的。

更新:添加代码和更多细节。

由于 Google API 对我的应用场景的限制,我将应用设计如下:

  1. 用户将从 Windows 窗体应用程序设置他们的 Google 帐户。 (“首次验证码”我在下面提到)
  2. 我将使用在第 1 步中从 Google 收到的令牌响应,在另一个应用程序中使用相同的客户端 ID/秘密(我在下面提到的“问题场景”代码)。因此,Google API 将查看令牌响应是否已经可用,如果可用,则不会显示身份验证页面并检查令牌是否已过期。如果过期,我将使用 RefreshTokenAsync 刷新令牌。

代码:

//First time authentication

 credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
                                        new ClientSecrets
                                        {
                                            ClientId,
                                            ClientSecret,
                                        },
                                        scopes,
                                        GoogleAuthUser,
                                        CancellationToken.None, new FileDataStore(filepath, true)).Result;

                var oauthSerivce = new Google.Apis.Oauth2.v2.Oauth2Service(new BaseClientService.Initializer()
                {
                    HttpClientInitializer = credential,
                    ApplicationName = "Appname",
                });

//Issue scenario:

credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
                    new ClientSecrets
                    {
                        ClientId,
                        ClientSecret,
                    },
                    Scopes,
                    "user",
                    CancellationToken.None,
                    new FileDataStore(filepath, true)).Result;

            if (credential.Token.IsExpired(credential.Flow.Clock))
            {
                if (credential.RefreshTokenAsync(CancellationToken.None).Result)
                {
                }
                else
                {
                }
            }
var service = new GmailService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = ApplicationName,
            });

异常详情:

 (Inner Exception #0) Google.Apis.Auth.OAuth2.Responses.TokenResponseException: Error:"invalid_request", Description:"Could not determine client ID from request.", Uri:""
   at Google.Apis.Auth.OAuth2.Responses.TokenResponse.<FromHttpResponseAsync>d__36.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Google.Apis.Auth.OAuth2.Requests.TokenRequestExtenstions.<ExecuteAsync>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Google.Apis.Auth.OAuth2.Flows.AuthorizationCodeFlow.<FetchTokenAsync>d__35.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Google.Apis.Auth.OAuth2.Flows.AuthorizationCodeFlow.<FetchTokenAsync>d__35.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Google.Apis.Auth.OAuth2.Flows.AuthorizationCodeFlow.<RefreshTokenAsync>d__31.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Google.Apis.Auth.OAuth2.UserCredential.<RefreshTokenAsync>d__22.MoveNext()<---

【问题讨论】:

  • 也许第 4 项:blog.timekit.io/…
  • 为什么不让客户端库为您刷新它,这就是它的设计目的。请编辑您的问题并包含minimal reproducible example。在我看来,要么您的客户端 ID 不正确,要么您尝试使用使用不同客户端 ID 创建的刷新令牌。
  • @jdweng 没有错误消息,它实际上表示刷新令牌已过期。
  • @DaImTo 如果客户端 ID 或刷新令牌错误,则应该始终出现该问题。正如我之前提到的,问题在测试期间随机发生。另外,我在问题中添加了更多细节。请看一下。

标签: c# oauth-2.0 google-oauth google-api-dotnet-client


【解决方案1】:

用户将通过 Windows 窗体应用设置他们的 Google 帐户。 (“首次验证码”我在下面提到)

实际上,除非您决定创建自己的 IDatastore 实现,否则您根本不会看到授权代码,自从您使用 fileDatastore 以来您似乎没有这样做。您没有在问题的任何地方提及授权码。

我将使用在第 1 步中从 Google 收到的令牌响应,并使用相同的客户端 ID/秘密(我在下面提到的“问题场景”代码)在不同的应用程序中。因此,Google API 将查看令牌响应是否已经可用,如果可用,则不会显示身份验证页面并检查令牌是否已过期。如果过期,我将使用 RefreshTokenAsync 刷新令牌。

实际上,GoogleWebAuthorizationBroker 通过检查您传递的用户表示的 fileDataStore 存储凭据的目录来处理所有这些。你的代码显然没有做你认为它在做的事情。

发布一个混合用户和混淆文件数据存储

我发现您的代码存在两个问题。首先是您正在混合用户。在以下代码中,您将存储名为 GoogleAuthUser 的用户的用户凭据。

credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
                                        new ClientSecrets
                                        {
                                            ClientId,
                                            ClientSecret,
                                        },
                                        scopes,
                                        GoogleAuthUser,
                                        CancellationToken.None, new FileDataStore(filepath, true)).Result;

在以下代码中,您正在为名为 user 的用户存储/读取 redetinals

credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
                    new ClientSecrets
                    {
                        ClientId,
                        ClientSecret,
                    },
                    Scopes,
                    "user",
                    CancellationToken.None,
                    new FileDataStore(filepath, true)).Result;

FileDataStore 将用户的凭据存储在您机器上的 %appdata% 中,并且用户由您发送的任何字符串表示。如果您在两个不同的调用中混合字符串,那么它将无法对用户进行身份验证。更多信息请咨询FileDatastore Demyistified

第二期已过期

客户端库旨在通过凭据对象处理授权,一旦将凭据对象加载到服务中,您就不必再担心它了,让库希望何时是刷新访问令牌的最佳时间

 if (credential.Token.IsExpired(credential.Flow.Clock))
            {
                if (credential.RefreshTokenAsync(CancellationToken.None).Result)
                {
                }
                else
                {
                }
            }

第三题

这到底是为了什么?

  var oauthSerivce = new Google.Apis.Oauth2.v2.Oauth2Service(new BaseClientService.Initializer()
                {
                    HttpClientInitializer = credential,
                    ApplicationName = "Appname",
                });

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-27
    • 2017-12-12
    • 1970-01-01
    • 1970-01-01
    • 2018-12-08
    • 2014-10-25
    • 1970-01-01
    • 2021-07-25
    相关资源
    最近更新 更多