I’m working on a web app that connects to Google Calendar. Most users can access their calendars just fine, but one specific user keeps getting an “invalid grant” error. This user is located in Australia and uses a custom domain email (not @gmail.com). The OAuth flow works correctly - they can authorize the app and I receive a valid access token. The user also grants calendar permissions during auth. But when I try to fetch their calendar list, the API throws an invalid grant error.
public class CalendarIntegration : ICalendarProvider {
private readonly IAuthRepository _authRepo;
private readonly CalendarConfig _config;
private const string APP_TITLE = "MyCalendarApp";
private const string ROLE_ADMIN = "owner";
private const string ROLE_EDITOR = "writer";
public CalendarIntegration(IAuthRepository authRepo, CalendarConfig config) {
_authRepo = authRepo;
_config = config;
}
private GoogleAuthorizationCodeFlow CreateAuthFlow() {
return new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer() {
ClientSecrets = CreateClientInfo(),
Scopes = GetRequiredScopes()
});
}
private CalendarService CreateService(AuthToken userToken) {
return new CalendarService(new BaseClientService.Initializer() {
ApplicationName = APP_TITLE,
HttpClientInitializer = CreateUserAuth(userToken)
});
}
private ClientSecrets CreateClientInfo() {
return new ClientSecrets() {
ClientId = _config.AppId,
ClientSecret = _config.AppSecret
};
}
private string[] GetRequiredScopes() {
return new[] { CalendarService.Scope.Calendar };
}
private UserCredential CreateUserAuth(AuthToken userToken) {
TokenResponse authResponse = new TokenResponse() {
AccessToken = userToken.Token,
RefreshToken = userToken.RefreshKey
};
return new UserCredential(CreateAuthFlow(), APP_TITLE, authResponse);
}
public async Task<List<CalendarInfo>> FetchEditableCalendars(Guid userId) {
AuthToken userAuth = await RetrieveToken(userId);
CalendarService service = CreateService(userAuth);
IList<CalendarListEntry> items = service.CalendarList
.List()
.Execute()
.Items;
return items.Where(item => item.AccessRole.Equals(ROLE_ADMIN, StringComparison.OrdinalIgnoreCase) ||
item.AccessRole.Equals(ROLE_EDITOR, StringComparison.OrdinalIgnoreCase))
.Select(item => new CalendarInfo() {
CalendarId = item.Id,
Title = item.Summary
})
.OrderBy(cal => cal.Title)
.ToList();
}
private async Task<AuthToken> RetrieveToken(Guid userId) {
AuthToken token = await _authRepo.FindAuthToken(userId);
if (token == null) {
throw new InvalidOperationException($"No auth token found for user: {userId}");
}
return token;
}
}