Google.GData.Client.GDataRequestException - Authentication suddenly fails in old code

Molecool picture Molecool · May 26, 2015 · Viewed 20.3k times · Source

I'm suddenly starting to get the following exception when attempting to authenticate and access a spreadsheet on Google drive:

Unhandled Exception: Google.GData.Client.GDataRequestException: Execution of aut hentication request returned unexpected result: 404 at Google.GData.Client.Utilities.getAuthException(TokenCollection tokens, Htt pWebResponse response) at Google.GData.Client.Utilities.QueryClientLoginToken(GDataCredentials gc, S tring serviceName, String applicationName, Boolean fUseKeepAlive, IWebProxy prox yServer, Uri clientLoginHandler) at Google.GData.Client.GDataGAuthRequest.QueryAuthToken(GDataCredentials gc) at Google.GData.Client.GDataGAuthRequest.EnsureCredentials() at Google.GData.Client.GDataRequest.EnsureWebRequest() at Google.GData.Client.GDataGAuthRequest.EnsureWebRequest() at Google.GData.Client.GDataRequest.Execute() at Google.GData.Client.GDataGAuthRequest.Execute(Int32 retryCounter) at Google.GData.Client.GDataGAuthRequest.Execute() at Google.GData.Client.Service.Query(Uri queryUri, DateTime ifModifiedSince, String etag, Int64& contentLength) at Google.GData.Client.Service.Query(FeedQuery feedQuery) at Google.GData.Documents.DocumentsService.Query(DocumentsListQuery feedQuery ) at GoogleLogger.GoogleService.getLastXECLogRows(String folderName, String fileName, Int32 rows)

This is in code that has been running for two years without any problems. I first thought that I may have lost access permissions on my production system but Google drive loads fine in my web browser. Tried it on several other systems and am getting the very same.

Did they change something in the Google API today? This can't be coincidence!

Answer

Alex Aza picture Alex Aza · May 28, 2015

Google has retired their older authentication API. OAuth 2.0 should be used instead.

I spent too much time to figure out how to use newer Auth API with older GData API grabbing bits and pieces of information here and there from the Internet. I decided to share all the the details with screenshots to save your time.

  1. Go to https://console.developers.google.com/project

  2. Hit Create Project button

enter image description here

  1. Create project. Type in some name.

enter image description here

  1. Go to API & Auth > Credentials and hit Create new Client ID button. It will create JSON key for you automatically - ignore that.

enter image description here

  1. Hit Generate new P12 key

enter image description here

  1. File download will start automatically. Remember the password, you will need it to open the file you just downloaded.

enter image description here

  1. Rename downloaded file to Key.p12 and add it to your solution. Make sure you set Build Action and Copy to Output Directory accordingly.

enter image description here

  1. Install Google API Auth using Nuget. Type the following in the Package Manager Console

    Install-Package Google.Apis.Auth
    

enter image description here

  1. Copy service account email address that was generated in Step #4.

enter image description here

  1. Grant appropriate permission to this user in your Google Spreadsheet.

  2. Use the following code to query the spreadsheet. Replace email and Google spreadsheet URL address in the code below.

    const string ServiceAccountEmail = "452351479-q41ce1720qd9l94s8847mhc0toao1fed@developer.gserviceaccount.com";
    
    var certificate = new X509Certificate2("Key.p12", "notasecret", X509KeyStorageFlags.Exportable);
    
    var serviceAccountCredentialInitializer = 
        new ServiceAccountCredential.Initializer(ServiceAccountEmail)
        {
            Scopes = new[] { "https://spreadsheets.google.com/feeds" }
        }.FromCertificate(certificate);
    
    var credential = new ServiceAccountCredential(serviceAccountCredentialInitializer);
    
    if (!credential.RequestAccessTokenAsync(System.Threading.CancellationToken.None).Result)
        throw new InvalidOperationException("Access token request failed.");
    
    var requestFactory = new GDataRequestFactory(null);
    requestFactory.CustomHeaders.Add("Authorization: Bearer " + credential.Token.AccessToken);
    
    var service = new SpreadsheetsService(null) { RequestFactory = requestFactory };
    
    var query = new ListQuery("https://spreadsheets.google.com/feeds/list/0ApZkobM61WIrdGRYshh345523VNsLWc/1/private/full");
    var feed = service.Query(query);
    
    var rows = feed.Entries
        .Cast<ListEntry>()
        .Select(arg =>
            new
            {
                Field0 = arg.Elements[0].Value,
                Field1 = arg.Elements[1].Value
            })
        .ToList();