Search code examples
c#google-cloud-platformgoogle-workspaceservice-accounts

Workspace Domain-wide Delegation with service account not working for one project, works for others


We have different projects on GCP we use them to access different Google APIs. Most of them for internal use only.

In this particular case, we have 2 projects, both use Service Account and both are allowed on Workspace Domain-wide Delegation on the same scopes. They are almost clones of each other.

I execute a simple request with the same code (Spreadsheet.Get()) with project 1 credentials it works. I execute the same request with project 2 credentials it doesn't work.

Since Workspace Domain-wide Delegation it's activated the spreadsheet its shared to my email and I connect to the API with my email too (works with project 1 so this is not the problem) (impersonating a user)

The only difference it's that one project has OAuth Consent Screen on external (only 100 users cause we use it internally only, anyways..) and the other one it's internal but this has nothing to do with this right?

Where the problem could come from? Do I need to recreate the project that doesn't work?

Here is the error message :

Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested

Edit to answer the comments but this code works depending on the service account we use

Generating the credentials:

internal static ServiceCredential GetApiCredentialsFromJson(string jsonCredentialsPath, string mailToMimic)
        {
            string jsonCertificate = File.ReadAllText(jsonCredentialsPath);
            string privateKey = Regex.Match(jsonCertificate, @"(?<=""private_key"": "")(.*)(?="")").Value.Replace(@"\n", "");
            string accountEmail = Regex.Match(jsonCertificate, @"(?<=""client_email"": "")(.*)(?="")").Value;

            ServiceAccountCredential.Initializer credentials = new ServiceAccountCredential.Initializer(accountEmail)
            {
                Scopes = _scopes,
                User = mailToMimic
            }.FromPrivateKey(privateKey);

            return new ServiceAccountCredential(credentials);
        }

Using the credentials:

internal GoogleSheetService(ServiceCredential credentials)
    {
        SheetsService = new SheetsService(new BaseClientService.Initializer()
        {
            HttpClientInitializer = credentials
        });
        SheetsService.HttpClient.Timeout = TimeSpan.FromSeconds(100);
    }

Client ID is allowed on the Drive, Ads and Spreadsheets scopes on the Workspace console.

enter image description here


Solution

  • The answer was simple, but we had to figure it out by ourselves.

    The scopes you add in your app when you initialize the client need to be exactly the same scopes you added in the Google Admin wide-delegation page. Even if your app or part of your app don't need them all.

    C# example:

            private static readonly string[] _scopes = { DriveService.Scope.Drive, SheetsService.Scope.Spreadsheets, SlidesService.Scope.Presentations };
    
            ServiceAccountCredential.Initializer credentials = new ServiceAccountCredential.Initializer(accountEmail)
            {
                Scopes = _scopes,
                User = mailToMimic
            }.FromPrivateKey(privateKey);
    
            return new ServiceAccountCredential(credentials);
    

    Here my app only needs SheetsService.Scope.Spreadsheets but I had to add DriveService.Scope.Drive and SlidesService.Scope.Presentations because the same client its used for other apps that need them.