Manuel Venè
Manuel Venè

Reputation: 77

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.

Upvotes: 1

Views: 687

Answers (2)

Amanda Tarafa Mas
Amanda Tarafa Mas

Reputation: 1138

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("user1@your-domain.com");

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.

Upvotes: 3

Manuel Venè
Manuel Venè

Reputation: 77

Thanks to John Hanley's comment I was able to find the solution. I assumed setting scopes at Domain-wide Delegation level was the way to go. I was wrong.

As John Hanley wrote I was "not specifying the OAuth scope for Google Drive access".

The following code achieves the needed result:

using var stream = new FileStream(credentialFileName, FileMode.Open, FileAccess.Read);
var credentials = ServiceAccountCredential.FromServiceAccountData(stream);
credentials.Scopes = new[]
{
    DriveService.Scope.Drive
};

The property credentials.Scopes can be set to include the scopes needed.

Now it works. Thanks again to John Hanley.

Upvotes: 0

Related Questions