0

I put together the example code below, using some helper code that came with Google's examples for its dot net google api client libraries.

The problem: It doesn't list any files info even though I uploaded one to the Gooogle account that owns this service account.

When I tried using oauth authorization to do the same thing, I got the expected result (I did that on this page: https://developers.google.com/drive/v2/reference/files/list ).

Maybe there is a problem with the scoping or something; any ideas?

My attempts at logging all the http requests and responses have completely failed. Google client libraries seem to have logging on a per-api basis, and I can't see any for google drive. Also using log4net with dotnetopenauth didn't do anything when I tried it (google's client libraries use dot net open auth).

Here is the code:

using System;
using System.Security.Cryptography.X509Certificates;
using Google.Apis.Authentication.OAuth2;
using Google.Apis.Authentication.OAuth2.DotNetOpenAuth;
using Google.Apis.Drive.v2;
using Google.Apis.Util;
using Google.Apis.Services;

namespace Drive.ServiceAccount
{
    class Program
    {

        static Program()
        {
//
            //ApplicationContext.RegisterLogger(new Log4NetLogger());
            log4net.Config.XmlConfigurator.Configure();          
        }

        private const string SERVICE_ACCOUNT_EMAIL = "<blablalba>@developer.gserviceaccount.com";
        private const string SERVICE_ACCOUNT_PKCS12_FILE_PATH = @"<blablalba>-privatekey.p12";

        /// <summary>
        /// Build a Drive service object authorized with the service account.
        /// </summary>
        /// <returns>Drive service object.</returns>
        static DriveService BuildService()
        {
            X509Certificate2 certificate = new X509Certificate2(SERVICE_ACCOUNT_PKCS12_FILE_PATH, "notasecret",
                X509KeyStorageFlags.Exportable);

            var _client = new AssertionFlowClient(GoogleAuthenticationServer.Description, certificate)
            {
                ServiceAccountId = SERVICE_ACCOUNT_EMAIL,
                Scope = DriveService.Scopes.Drive.GetStringValue(),
            };
            var auth = new OAuth2Authenticator<AssertionFlowClient>(_client, AssertionFlowClient.GetState);

            var service = new DriveService(new BaseClientService.Initializer()
            {
                Authenticator = auth,
                ApplicationName = "Drive API Sample",
            });

            return service;
        }

        static void Main(string[] args)
        {
            var _driveService = BuildService();

            var _files = _driveService.Files.List().Execute();

            foreach (var _file in _files.Items)
            {
                Console.WriteLine(_file.Id);
            }

            Console.WriteLine("Done");

            Console.ReadLine();
        }

    }
}

Thanks to Robadob for correcting my misunderstanding about service accounts. Here is some working code (just the main method with everything else as above):

static void Main(string[] args)
{

    String _newFileName = Guid.NewGuid().ToString() + ".xml";

    new XDocument( new XElement("root", new XElement("someNode", "someValue")))
        .Save(_newFileName);

    Console.WriteLine("New file saved locally with name {0}.", _newFileName);

    var _driveService = BuildService();

    // upload the file:

    //var uploadStream = 
    //    new System.IO.FileStream(_newFileName, System.IO.FileMode.Open, System.IO.FileAccess.Read);

    byte[] byteArray = System.IO.File.ReadAllBytes(_newFileName);
    MemoryStream _byteStream = new MemoryStream(byteArray);

    var insert = _driveService.Files.Insert(new Google.Apis.Drive.v2.Data.File
    {
        Title = _newFileName,
    }, _byteStream, "text/xml");

    Task _uploadTask = insert.UploadAsync();

    _uploadTask.ContinueWith(t => Console.WriteLine("Upload Task Completed"),TaskContinuationOptions.OnlyOnRanToCompletion);
    //

    try
    {
        _uploadTask.Wait();
    }
    catch (AggregateException _ex)
    {
        foreach (Exception ex in _ex.Flatten().InnerExceptions)
        {
            Console.WriteLine(ex.Message);
        }
    }

    Console.WriteLine("Reading files");

    var _files = _driveService.Files.List();


    foreach (var _file in _files.Execute().Items)
    {
        Console.WriteLine(_file.Id);
    }

    Console.WriteLine("Done");

    Console.ReadLine();
}
Elliot
  • 2,002
  • 1
  • 20
  • 20

1 Answers1

0

As it is a service account I don't believe it has its own 'Google Drive', service accounts exist to act on behalf of a user.

To 'impersonate' the user whose files you wish to list, you need to pass a prn parameter when you generate the access token;

Details can be found here if you scroll down to 'Additional Claims'; https://developers.google.com/accounts/docs/OAuth2ServiceAccount#formingclaimset

It appears as though you can set the user via this;

        var _client = new AssertionFlowClient(GoogleAuthenticationServer.Description, certificate)
        {
            ServiceAccountId = SERVICE_ACCOUNT_EMAIL,
            Scope = DriveService.Scopes.Drive.GetStringValue(),
            ServiceAccountUser = "Name@Company.com"
        };

If you wish to log the HTTP interactions, I have found Fiddler to be much easier to setup than other methods.

Robadob
  • 5,319
  • 2
  • 23
  • 32
  • Damn, I heard about the impersonation thing, but I thought that the other idea of a service account is that that the app could have its own resources and access them. – Elliot Aug 06 '13 at 12:36
  • It possibly can (I haven't used Google drive API), however in your question you say you added a file to the user who owns the service account (not the the service accounts drive). I wouldn't expect you can access the service accounts files not through the API, so try adding a file via the service account via the API. – Robadob Aug 06 '13 at 12:40
  • OK thanks; it might be clear now. The service account is separate from the google account in whose console I created the service account. Thankyou for the help. – Elliot Aug 06 '13 at 12:45