Reputation: 77
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
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
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