TokenResponseException - Error:"invalid_grant", Description:"", Uri:""

TokenResponseException - Error:"invalid_grant", Description:"", Uri:""

我有可用的代码 - MVC 应用程序使用 Google 日历 API 和 Gmail API 以及来自 Google 的 OAuth2 身份验证。该代码有效。加载页面时,将显示来自两个服务的数据。我有一个 Javascript 计时器以一定的时间间隔(20 分钟)刷新数据。所以一切都按预期工作,直到某个时间点(我猜是在某个时间间隔之后)它开始抛出异常:Error:"invalid_grant", Description:"", Uri:"".该异常没有 InnerException,只有该错误消息和 StackTrace 中的此信息(屏幕截图此处):

如果有人知道该错误的原因是什么,我将不胜感激。堆栈跟踪消息中的 "c:\code\google.com...." 行是什么,我的磁盘上没有 "c:\code" 文件夹。我发现了一些与相同错误相关的帖子,但不幸的是 它们对理解问题没有帮助。也许有了像这张截图这样的更多细节,有人可以获得关于这个主题的更多信息。非常感谢。

我发现 - AppPool 回收 temporary 解决了这个问题。但过了一段时间,它又回来了。和AppPool回收有什么关系?

好吧,经过更多的阅读,我找到了这个异常的原因。

https://developers.google.com/accounts/docs/OAuth2#expiration

https://developers.google.com/analytics/devguides/config/mgmt/v3/mgmtAuthorization?#helpme

无效授予:已超过刷新令牌限制(默认为 25)。 就这些了。

根据这些文档:目前每个 Google 用户帐户有 25 个令牌的限制。如果用户帐户有 25 个有效令牌,下一个身份验证请求会成功,但会在没有任何用户可见警告的情况下安静地使最旧的未完成令牌无效。

如果应用程序尝试使用无效的刷新令牌,则会返回 invalid_grant 错误响应。每对唯一的 OAuth 2.0 客户端和 Google Analytics 帐户的限制是 25 个刷新令牌(请注意,此限制可能会更改)。

明白了,他们将刷新令牌的数量限制为 25,但他们没有说明当您需要超过该限制时该怎么做。啊……我一直在试验并找到了绕过该限制的解决方案。似乎确实回收应用程序池解决了问题(当然直到达到下一个 25 限制)。我们可以从 IIS 手动回收 AppPool 或者通过 运行 命令:

c:\Windows\System32\inetsrv\appcmd.exe recycle apppool /apppool.name:AppPoolName

您可以安排该命令在每晚或每小时执行,任何...

但我发现了一个程序化的解决方案:

为您的控制器覆盖 OnException 方法(它用于 MVC 应用程序)

protected override void OnException(ExceptionContext filterContext)
{
    if (filterContext.ExceptionHandled) return;

    // Log exception details
    Global.LogException(filterContext.Exception, EventLogEntryType.Error);

    if (filterContext.Exception.Message.Contains("invalid_grant"))
    {
        // Invalid Grant: The refresh token limit has been exceeded (default is 25).
        // https://developers.google.com/accounts/docs/OAuth2#expiration
        // https://developers.google.com/analytics/devguides/config/mgmt/v3/mgmtAuthorization?#helpme

        Global.RecycleAppPool();
        Global.LogException(new Exception("AppPool has been recycled"), EventLogEntryType.Information);
        Response.Redirect("Index");
    }

    var actionName = filterContext.RouteData.Values["action"].ToString();

    // Return friendly error message
    var errorMessage = string.Format("Action {0} failed with error: {1}. Please try again.", actionName, filterContext.Exception.Message);
    filterContext.Result = Content(errorMessage);

    filterContext.ExceptionHandled = true;
    base.OnException(filterContext);
}

其中RecycleAppPool是这样定义的(这个操作很快,不像重启IIS:):

public static void RecycleAppPool()
{
    ServerManager serverManager = new ServerManager();
    ApplicationPool appPool = serverManager.ApplicationPools["Homepage"];
    if (appPool != null)
    {
        if (appPool.State == ObjectState.Stopped) appPool.Start();
        else appPool.Recycle();
    }
}

因此,在 invalid_grant 异常的情况下,异常 "swallowed":已记录,apppool 被回收并且刷新令牌的限制被重置。希望这有帮助。

如果您发现一些问题,请告诉我。

也有可能是服务器时钟不同步。出于某种原因,我的时钟无法与互联网时钟同步,快了 运行 6 分钟。将其重置为正确的时间有效。