刷新令牌时无法从请求错误中确定客户端 ID
Posted
技术标签:
【中文标题】刷新令牌时无法从请求错误中确定客户端 ID【英文标题】:Could not determine client ID from request error while refreshing token 【发布时间】: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 对我的应用场景的限制,我将应用设计如下:
-
用户将从 Windows 窗体应用程序设置他们的 Google 帐户。 (“首次验证码”我在下面提到)
我将使用在第 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 或刷新令牌错误,则应该始终出现该问题。正如我之前提到的,问题在测试期间随机发生。另外,我在问题中添加了更多细节。请看一下。 【参考方案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",
);
【讨论】:
以上是关于刷新令牌时无法从请求错误中确定客户端 ID的主要内容,如果未能解决你的问题,请参考以下文章