6

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

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
}
fizch
  • 2,599
  • 3
  • 30
  • 45
  • Did you try to generate again the tokens with the new scopes? Remember that Access tokens are valid only for the set of operations and resources described in the scope of the token request. – Alessandro May 11 '20 at 09:54
  • Yes. I have deleted it and recreated it several times during this process. – fizch May 11 '20 at 16:23
  • What `client_type` has the client you are not able to authenticate? – Alessandro May 12 '20 at 14:00
  • I do not see a spot to set that. I am not sure what setting i should use there if it was there. – fizch May 18 '20 at 19:24
  • I just tried creating a new client id as well. I picked up a new token for my user with that client id and still got the same results. The oauth process is successful. Just trying to read the list of calendars (or getting an individual) gives me the invalid_grant with bad request. – fizch May 19 '20 at 02:47
  • This is what I am referring to: https://stackoverflow.com/a/19041051/7453656. Even if you cannot see how to set that I would give it a try anyways. – Alessandro May 19 '20 at 07:27
  • I think you are talking about trying to use an email address as the client id. That's what I was directed to you by your link. I do not have an email address listed like others are mentioning in the comments. My app uses a OAuth 2 client ID and offline access. – fizch May 19 '20 at 15:15
  • this appears to be happening during the authentication phase of the transaction. I am not seeing any errors in the dashboard for the calendar list method. I have tried wiring up error handler and http unsuccessful handlers against the API. Neither of those things are being called. – fizch May 20 '20 at 02:18
  • 1
    Something that's helped me immensely in situations like yours is to use the Google Developer OAuth Playground https://developers.google.com/oauthplayground/. 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. – IVSoftware May 21 '20 at 16:49
  • @IVSoftware please post this as the answer. I was able to determine the problem by using this tool. – fizch May 21 '20 at 18:18

1 Answers1

6

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.

IVSoftware
  • 5,732
  • 2
  • 12
  • 23
  • 1
    I was able to use this playground to determine that I was truncating the access and refresh tokens in my database. I don't recall seeing any kind of description of how long those tokens could get so I felt pretty safe at 100 characters. When using this tool, I was getting back tokens that were 170 characters in length. After changing those columns in the database and updating the data, my calls started working. – fizch May 22 '20 at 03:31
  • Hey, and kudos to you for spotting what sounds like a really subtle and insidious bug! – IVSoftware May 22 '20 at 04:24