You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Google.Apis.Auth.AspNetCore3调用Gmail API时Token过期或被吊销问题求助

问题成因
  • 核心原因是你的OpenID Connect配置未显式要求返回刷新令牌:Google OAuth默认只返回有效期1小时的访问令牌(access_token),只有显式设置access_type=offline才会返回可用于刷新访问令牌的刷新令牌(refresh_token),你当前配置缺少该参数,拿到的refresh_token大概率为空或无效,自然无法完成令牌刷新。
  • 令牌未持久化更新:你每次调用Gmail API时都从Cookie中读取首次登录时存储的旧令牌,即便某次刷新成功,新的令牌也没有更新回Cookie的身份认证属性中,下一次调用依然会使用已过期的旧令牌,导致报错。
  • 测试应用令牌有效期限制:如果你的Google Cloud应用处于测试状态,刷新令牌默认有效期只有7天,不过你1小时就触发报错,说明核心问题还是前两点。
修复方案

步骤1:修改Startup.cs中的OAuth配置

新增三个必要配置项,确保能拿到有效刷新令牌且令牌会被自动存入Cookie:

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddGoogleOpenIdConnect(options =>
{
    var secrets = GoogleClientSecrets.FromFile("google_client_secret.json").Secrets;
    options.Scope.Add(GmailService.Scope.GmailSend);
    options.Scope.Add(GmailService.Scope.GmailSettingsSharing);
    options.Scope.Add(GmailService.Scope.GmailSettingsBasic);
    options.ClientId = secrets.ClientId;
    options.ClientSecret = secrets.ClientSecret;
    options.CallbackPath = new PathString("/signin-google");
    // 新增以下3行配置
    options.AccessType = "offline";
    options.SaveTokens = true;
    options.Prompt = "consent";
});

步骤2:优化Gmail API调用逻辑,主动更新Cookie中的令牌

在调用API前先判断访问令牌是否即将过期,刷新成功后将新令牌写回Cookie,避免后续调用使用旧令牌:

AuthenticateResult authResult = await HttpContext.AuthenticateAsync();
if (!authResult.Succeeded)
{
    return Challenge(GoogleOpenIdConnectDefaults.AuthenticationScheme);
}

string accessToken = authResult.Properties.GetTokenValue(OpenIdConnectParameterNames.AccessToken);
string refreshToken = authResult.Properties.GetTokenValue(OpenIdConnectParameterNames.RefreshToken);
string expiresAtStr = authResult.Properties.GetTokenValue("expires_at");
DateTimeOffset expiresAt = DateTimeOffset.Parse(expiresAtStr);

var secrets = GoogleClientSecrets.FromFile("google_client_secret.json").Secrets;
string googleAuthTokenPath = "GoogleAuthToken";

// 提前5分钟判断令牌是否即将过期,主动刷新
if (expiresAt <= DateTimeOffset.UtcNow.AddMinutes(5))
{
    var flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
    {
        ClientSecrets = secrets,
        DataStore = new FileDataStore(googleAuthTokenPath, true),
    });
    // 刷新获取新令牌
    var newToken = await flow.RefreshTokenAsync("user", refreshToken, CancellationToken.None);
    // 更新Cookie中存储的令牌信息
    authResult.Properties.UpdateTokenValue(OpenIdConnectParameterNames.AccessToken, newToken.AccessToken);
    authResult.Properties.UpdateTokenValue("expires_at", DateTimeOffset.UtcNow.AddSeconds(newToken.ExpiresInSeconds.Value).ToString("o"));
    if (!string.IsNullOrEmpty(newToken.RefreshToken))
    {
        authResult.Properties.UpdateTokenValue(OpenIdConnectParameterNames.RefreshToken, newToken.RefreshToken);
    }
    // 重新签入更新Cookie
    await HttpContext.SignInAsync(authResult.Principal, authResult.Properties);
    // 更新当前上下文使用的令牌
    accessToken = newToken.AccessToken;
    refreshToken = newToken.RefreshToken ?? refreshToken;
}

var credentials = new UserCredential(new GoogleAuthorizationCodeFlow(
new GoogleAuthorizationCodeFlow.Initializer
{
 ClientSecrets = secrets,
 DataStore = new FileDataStore(googleAuthTokenPath, true),
}),
"user",
new TokenResponse { AccessToken = accessToken, RefreshToken = refreshToken }
);

var service = new GmailService(new BaseClientService.Initializer()
{
 HttpClientInitializer = credentials,
 ApplicationName = "Some app",
});

额外注意事项

  • 如果你的Google Cloud应用目前处于测试状态,需在OAuth同意屏幕页面将应用发布为正式版,否则刷新令牌7天就会过期失效。
  • 敏感Gmail Scope需要在Google Cloud控制台完成OAuth应用验证,未验证的应用会有功能和有效期限制。
  • 不要频繁调用刷新接口,短时间内多次使用同一个刷新令牌请求新令牌可能触发Google的安全机制,导致刷新令牌被撤回。

内容的提问来源于stack exchange,提问作者dasisderblyme

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 00:15:04