Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google Calendar API returns invalid_grant and bad request

In my development environment, I have a user that I just received an OAuth Token for the following scopes.

  • https://www.googleapis.com/auth/calendar
  • https://www.googleapis.com/auth/calendar.events
  • https://www.googleapis.com/auth/calendar.readonly

Everything looks fine and I store the token for the user. I then request to list the calendars for the user and I get the invalid_grant with bad request. I try the same request with another user's token (also in my development environment) and it works correctly.

I originally had only the first scope setup, which is write level access. That is what all existing tokens were created with. During my testing, I added the other scopes.

I have tried updating the NuGet packages for Google APIs in my project.

This is my class that is making the calls.

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
}
like image 399
fizch Avatar asked Oct 16 '22 03:10

fizch


1 Answers

Something that's helped me immensely in situations like the one you describe is to use the Google Developer OAuth Playground. By default, you can obtain the grant (and watch the traffic) using OAuthPlayground itself as the client. But then the trick is to go in the [Settings] gear and check the box for [x] Use your own OAuth Credentials and try and authorize your client. IMO this is a very useful debugging tool and I wanted to make sure you are aware of it.

like image 146
IVSoftware Avatar answered Oct 19 '22 00:10

IVSoftware