Google 日历 API returns invalid_grant 和错误请求
Google Calendar API returns invalid_grant and bad request
在我的开发环境中,我有一个用户刚刚收到以下范围的 OAuth 令牌。
- https://www.googleapis.com/auth/calendar
- https://www.googleapis.com/auth/calendar.events
- https://www.googleapis.com/auth/calendar.readonly
一切正常,我为用户存储了令牌。然后我请求列出用户的日历,我得到 invalid_grant 错误的请求。我用另一个用户的令牌(也在我的开发环境中)尝试了相同的请求并且它工作正常。
我最初只有第一个作用域设置,即写入级别访问权限。这就是创建所有现有令牌的原因。在我的测试过程中,我添加了其他范围。
我已尝试为我的项目中的 Google API 更新 NuGet 包。
这是我的 class 正在打电话。
public class GoogleCalendarAdapter : ICalendarAdapter {
#region attributes
private readonly ISiteAuthTokenQueryRepository _tokenRepo;
private readonly GoogleCalendarSettings _settings;
private const string APPNAME = "REDACTED";
private const string ACL_OWNER = "owner";
private const string ACL_WRITER = "writer";
#endregion
#region ctor
public GoogleCalendarAdapter(ISiteAuthTokenQueryRepository tokenRepo,
GoogleCalendarSettings settings) {
_tokenRepo = tokenRepo;
_settings = settings;
}
#endregion
#region methods
private GoogleAuthorizationCodeFlow BuildAuthorizationCodeFlow() {
return new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer() {
ClientSecrets = BuildClientSecrets(),
Scopes = BuildScopeList()
});
}
private CalendarService BuildCalendarService(SiteAuthToken token) {
return new CalendarService(new BaseClientService.Initializer() {
ApplicationName = APPNAME,
HttpClientInitializer = BuildUserCredential(token)
});
}
private ClientSecrets BuildClientSecrets() {
return new ClientSecrets() {
ClientId = _settings.ClientId,
ClientSecret = _settings.ClientSecret
};
}
private string[] BuildScopeList() {
return new [] { CalendarService.Scope.Calendar };
}
private UserCredential BuildUserCredential(SiteAuthToken token) {
TokenResponse responseToken = new TokenResponse() {
AccessToken = token.AccessToken,
RefreshToken = token.RefreshToken
};
return new UserCredential(BuildAuthorizationCodeFlow(), APPNAME, responseToken);
}
public async Task<List<Cal>> GetAllWritableCalendars(Guid siteGuid) {
SiteAuthToken token = await GetToken(siteGuid);
CalendarService svc = BuildCalendarService(token);
IList<CalendarListEntry> calendars = svc.CalendarList
.List()
.Execute()
.Items;
return calendars.Where(c => c.AccessRole.Equals(ACL_OWNER, StringComparison.CurrentCultureIgnoreCase) ||
c.AccessRole.Equals(ACL_WRITER, StringComparison.CurrentCultureIgnoreCase))
.Select(c => new Cal() {
Id = c.Id,
Name = c.Summary
})
.OrderBy(o => o.Name)
.ToList();
}
public async Task<Cal> GetCalendar(Guid siteGuid, string calendarId) {
SiteAuthToken token = await GetToken(siteGuid);
CalendarService svc = BuildCalendarService(token);
CalendarListEntry entry = svc.CalendarList
.Get(calendarId)
.Execute();
Cal retVal = new Cal() {
Id = entry.Id,
Name = entry.Summary
};
return retVal;
}
private async Task<SiteAuthToken> GetToken(Guid siteGuid) {
SiteAuthToken retVal = await _tokenRepo.GetSiteAuthToken(siteGuid, Constants.OAUTH_PROVIDER_GOOGLE);
if (retVal == null) {
throw new ApplicationException($"Could not find a SiteAuthToken for specified site (SiteGuid: {siteGuid})");
}
return retVal;
}
#endregion
}
在您描述的这种情况下,对我帮助很大的是使用 Google 开发人员 OAuth Playground。默认情况下,您可以使用 OAuthPlayground 本身作为客户端来获得授权(并观察流量)。但诀窍是进入 [Settings] 齿轮并选中 [x] Use your own OAuth Credentials 的框并尝试授权您的客户端。 IMO 这是一个非常有用的调试工具,我想确保你知道它。
在我的开发环境中,我有一个用户刚刚收到以下范围的 OAuth 令牌。
- https://www.googleapis.com/auth/calendar
- https://www.googleapis.com/auth/calendar.events
- https://www.googleapis.com/auth/calendar.readonly
一切正常,我为用户存储了令牌。然后我请求列出用户的日历,我得到 invalid_grant 错误的请求。我用另一个用户的令牌(也在我的开发环境中)尝试了相同的请求并且它工作正常。
我最初只有第一个作用域设置,即写入级别访问权限。这就是创建所有现有令牌的原因。在我的测试过程中,我添加了其他范围。
我已尝试为我的项目中的 Google API 更新 NuGet 包。
这是我的 class 正在打电话。
public class GoogleCalendarAdapter : ICalendarAdapter {
#region attributes
private readonly ISiteAuthTokenQueryRepository _tokenRepo;
private readonly GoogleCalendarSettings _settings;
private const string APPNAME = "REDACTED";
private const string ACL_OWNER = "owner";
private const string ACL_WRITER = "writer";
#endregion
#region ctor
public GoogleCalendarAdapter(ISiteAuthTokenQueryRepository tokenRepo,
GoogleCalendarSettings settings) {
_tokenRepo = tokenRepo;
_settings = settings;
}
#endregion
#region methods
private GoogleAuthorizationCodeFlow BuildAuthorizationCodeFlow() {
return new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer() {
ClientSecrets = BuildClientSecrets(),
Scopes = BuildScopeList()
});
}
private CalendarService BuildCalendarService(SiteAuthToken token) {
return new CalendarService(new BaseClientService.Initializer() {
ApplicationName = APPNAME,
HttpClientInitializer = BuildUserCredential(token)
});
}
private ClientSecrets BuildClientSecrets() {
return new ClientSecrets() {
ClientId = _settings.ClientId,
ClientSecret = _settings.ClientSecret
};
}
private string[] BuildScopeList() {
return new [] { CalendarService.Scope.Calendar };
}
private UserCredential BuildUserCredential(SiteAuthToken token) {
TokenResponse responseToken = new TokenResponse() {
AccessToken = token.AccessToken,
RefreshToken = token.RefreshToken
};
return new UserCredential(BuildAuthorizationCodeFlow(), APPNAME, responseToken);
}
public async Task<List<Cal>> GetAllWritableCalendars(Guid siteGuid) {
SiteAuthToken token = await GetToken(siteGuid);
CalendarService svc = BuildCalendarService(token);
IList<CalendarListEntry> calendars = svc.CalendarList
.List()
.Execute()
.Items;
return calendars.Where(c => c.AccessRole.Equals(ACL_OWNER, StringComparison.CurrentCultureIgnoreCase) ||
c.AccessRole.Equals(ACL_WRITER, StringComparison.CurrentCultureIgnoreCase))
.Select(c => new Cal() {
Id = c.Id,
Name = c.Summary
})
.OrderBy(o => o.Name)
.ToList();
}
public async Task<Cal> GetCalendar(Guid siteGuid, string calendarId) {
SiteAuthToken token = await GetToken(siteGuid);
CalendarService svc = BuildCalendarService(token);
CalendarListEntry entry = svc.CalendarList
.Get(calendarId)
.Execute();
Cal retVal = new Cal() {
Id = entry.Id,
Name = entry.Summary
};
return retVal;
}
private async Task<SiteAuthToken> GetToken(Guid siteGuid) {
SiteAuthToken retVal = await _tokenRepo.GetSiteAuthToken(siteGuid, Constants.OAUTH_PROVIDER_GOOGLE);
if (retVal == null) {
throw new ApplicationException($"Could not find a SiteAuthToken for specified site (SiteGuid: {siteGuid})");
}
return retVal;
}
#endregion
}
在您描述的这种情况下,对我帮助很大的是使用 Google 开发人员 OAuth Playground。默认情况下,您可以使用 OAuthPlayground 本身作为客户端来获得授权(并观察流量)。但诀窍是进入 [Settings] 齿轮并选中 [x] Use your own OAuth Credentials 的框并尝试授权您的客户端。 IMO 这是一个非常有用的调试工具,我想确保你知道它。