Search code examples
c#google-cloud-platformgoogle-drive-apigoogle-oauth

Service Account authentication credentials for Google Drive not working


I create credentials for google service account from json file as follow:

using var stream = new FileStream(credentialFileName, FileMode.Open, FileAccess.Read);
var credentials = ServiceAccountCredential.FromServiceAccountData(stream);

The service account has Domain-wide Delegation with scopes:

The result is then used to create a DriveService or a SheetsService object.

sheetService = new SheetsService(new BaseClientService.Initializer
{
    HttpClientInitializer = credentials
});
driveService = new DriveService(new BaseClientService.Initializer
{
    HttpClientInitializer = credentials
});

I've been succesfully interacting with Google Sheets API. Writing and reading without any issue, it just works fine.

As soon as I try to interact with Google Drive API things don't work because of lack of authentication. This code is an example:

var fileListRequest = DriveService.Files.List();
fileListRequest.Q = $"'{gDriveFolderID}' in parents";
var fileList = await fileListRequest.ExecuteAsync();

I get exception: "The service drive has thrown an exception. HttpStatusCode is Unauthorized. Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project"

If instead of authenticating with a service account I authenticate with an ID client OAuth 2.0, which requires me to accept in a browser page, it works. The problem is that this authentication expires.

I Know I could use the refresh token. But it also will expire at some point, and service accounts exist exactly to avoid refreshing authentication when dealing with internal data.


Solution

  • Your code is not complete, you are neither scoping your credentials nor using user impersonation.

    var credentials = GoogleCredential.FromFile(credentialFileName)
        .CreateScoped(
            "https://www.googleapis.com/auth/drive", 
            "https://www.googleapis.com/auth/spreadsheets")
         // This is the email of the user in the domain where
         // the service account has domain wide delegation,
         // that you want to manipulate Drive files/Sheets for.
        .CreateWithUser("[email protected]");
    
    var sheetService = new SheetsService(new BaseClientService.Initializer
    {
        HttpClientInitializer = credentials
    });
    var driveService = new DriveService(new BaseClientService.Initializer
    {
        HttpClientInitializer = credentials
    });
    

    I'm not certain why it was working for Sheets before though.