0

I'm using the Google APIs for .NET. I'm following the example project Simple OAuth2 but I keep getting a Protocol Exception from DotNetOpenAuth.

Here's what I have now:

public static void Main( string[] args )
{
   // Register the authenticator.
   NativeApplicationClient provider = new NativeApplicationClient(
      GoogleAuthenticationServer.Description, gaAppId, gaSecret );
   OAuth2Authenticator<NativeApplicationClient> auth = new OAuth2Authenticator<NativeApplicationClient>(
      provider, GetAuthorization );

   AnalyticsService analyticsService =
      new AnalyticsService( new BaseClientService.Initializer {
         Authenticator = auth,
         ApplicationName = @"Test Application",
      } );
   DataResource.GaResource.GetRequest request = analyticsService.Data.Ga.Get(
      gaId, @"2013-09-04", @"2013-09-18", "ga:totalEvents" );
   GaData data = request.Execute();

   Console.ReadKey();
}

private static IAuthorizationState GetAuthorization( NativeApplicationClient arg )
{
   // Get the auth URL:
   IAuthorizationState state =
      new AuthorizationState( new[] {AnalyticsService.Scopes.AnalyticsReadonly.GetStringValue()} );
   state.Callback = new Uri( NativeApplicationClient.OutOfBandCallbackUrl );

   // Retrieve the access token by using the authorization code:
   return arg.ProcessUserAuthorization( authCode, state );
}

Notes: I'm using the Analytics API in this code because that's what I need. I get the same error when using the Tasks API as described in the sample.

The authentication code is a refresher token generated by the process as defined in the example code. The error comes in both cases (request a new token, or re-use an old one.)

The ProtocolException that is triggered by DotNetOpenAuth is there because accounts.google.com returns an error: invalid request.

Here's what the OAuth request looks like:

Aplication/x-www-form-urlencoded; charset=utf-8
User-Agent: DotNetOpenAuth/4.3.1.13153
Host: accounts.google.com
Cache-Control: no-store,no-cache
Pragma: no-cache
Content-Length: 148
Connection: Keep-Alive

code=REDACTED&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&grant_type=authorization_code

And this is what the Google returns:

HTTP/1.1 400 Bad Request
IRRELEVANT_HEADERS
{
  "error" : "invalid_request"
}

I'm at a bit of a loss here as to why this happend and how I can solve this. I can't find any other working C# examples. It seems to be a different kind of error than the one in this thread. Is there any of you who knows how I can fix this?

Community
  • 1
  • 1
Huppie
  • 11,263
  • 4
  • 32
  • 34
  • Did you try to run of our samples in our samples repository, let's say (Task sample - https://code.google.com/p/google-api-dotnet-client/source/browse/Tasks.SimpleOAuth2/README.html?repo=samples). It should work for you, follow the README instructions and let me know. After the work, check what is the diff between the working sample and your code. Good luck! – peleyal Sep 19 '13 at 12:36
  • @peleyal I ran the samples from the repository and they seem to work. There must be a bug in my code somewhere... I noticed that the example doesn't use an offline refresher-token, and using a refresher token (by adding access_type=offline&approval_prompt=force to the authorization request url) doesn't work. I'll have a closer look at the examples and see if I can find the differences or if I can find an example that does use a refresher token. – Huppie Sep 24 '13 at 07:15

1 Answers1

0

I studied the examples provided by peleyal some more and managed to find the issue. The problem was that I wasn't actually storing a refresh-token, I was storing an authentication token. I should've used the authentication token to generate the refresh token.

Here's the full solution in a simplified manner for future reference. The differences can be found in the GetAuthorization function, which now correctly saves the refresh token and opens a browser window to request the authorization when a refresh token is not yet available.

private static refreshToken = null;

public static void Main( string[] args )
{
    // Register the authenticator.
    NativeApplicationClient provider = new NativeApplicationClient(
    GoogleAuthenticationServer.Description, gaAppId, gaSecret );
    OAuth2Authenticator<NativeApplicationClient> auth = new OAuth2Authenticator<NativeApplicationClient>(
        provider, GetAuthorization );

    AnalyticsService analyticsService = new AnalyticsService( new BaseClientService.Initializer {
        Authenticator = auth,
        ApplicationName = @"Test Application",
    } );
    DataResource.GaResource.GetRequest request = analyticsService.Data.Ga.Get( gaId, @"2013-09-04",
        @"2013-09-18", @"ga:totalEvents" );
    GaData data = request.Execute();

    Console.ReadKey();
}

private static IAuthorizationState GetAuthorization( NativeApplicationClient arg )
{
    // Get the auth URL:
    IAuthorizationState state = new AuthorizationState(
        new[] {AnalyticsService.Scopes.AnalyticsReadonly.GetStringValue()} );
    state.Callback = new Uri( NativeApplicationClient.OutOfBandCallbackUrl );

    if( !string.IsNullOrEmpty( refreshToken ) ) {
        try {
            state.RefreshToken = refreshToken;
            arg.RefreshToken( state );
        } catch {
            refreshToken = null;
        }
    }

    // If the refresh token is empty, request a new one by opening
    // a browser window. Allows the user to paste its authorization token
    // and saves the refresh token.
    if( string.IsNullOrEmpty( refreshToken ) ) {
        Uri authUri = arg.RequestUserAuthorization( state );

        // Request authorization from the user (by opening a browser window):
        Process.Start( authUri.ToString() );
        Console.Write( "  Authorization Code: " );
        string authCode = Console.ReadLine();
        Console.WriteLine();

        // Retrieve the access token by using the authorization code:
        state = arg.ProcessUserAuthorization( authCode, state );
        refreshToken = state.RefreshToken;
    }

    return state;
}
Huppie
  • 11,263
  • 4
  • 32
  • 34