Integrating with Google Admin SDK in C#
Asked Answered
C

3

7

I'm currently trying to integrate with the Google Admin SDK via C# so we can manage users via our own system. However, when running the project I get the error: Unauthorized Client.

Things I have already done via a super admin account:

Here's the code that i'm using.

ServiceAccountCredential credential = new ServiceAccountCredential(
                new ServiceAccountCredential.Initializer(_googleServiceSettings.Client_Email)
                {
                    ProjectId = _googleServiceSettings.Project_Id,
                    User = "[email protected]",
                    Scopes = new[] { DirectoryService.Scope.AdminDirectoryUser }
                }.FromPrivateKey(_googleServiceSettings.Private_Key));

            var service = new DirectoryService(new BaseClientService.Initializer
            {
                HttpClientInitializer = credential,
                ApplicationName = "Test API"
            });

            var request = service.Users.Get("[email protected]");
            var result = await request.ExecuteAsync();

The full error i'm getting is

An unhandled exception has occurred while executing the request. Google.Apis.Auth.OAuth2.Responses.TokenResponseException: Error:"unauthorized_client", Description:"Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.", Uri:""

Cheshvan answered 1/5, 2019 at 15:14 Comment(0)
S
6

Example code that will print some information about a user.

The important item is the class Google.Apis.Admin.Directory.directory_v1.Data.User

Documentation link.

Your error is caused by not creating the credentials correctly. Usually, an issue with scopes when creating the credentials. I am assuming that you have Domain-Wide Delegation setup correctly for the service account.

I am also assuming that the user that you are impersonating is a G Suite Super Admin. If not, you will see a 403 error for service.Users.Get().

The file service_account.json is a normal JSON file that you downloaded from the Google Console (or created with gcloud).

The user [email protected] is the email address for the G Suite user for which information will be displayed.

The user [email protected] is the G Suite Super Admin.

using Google.Apis.Auth.OAuth2;
using Google.Apis.Admin.Directory.directory_v1;
using Google.Apis.Admin.Directory.directory_v1.Data;
using Google.Apis.Services;
using System;
using System.IO;

// dotnet add package Google.Apis.Admin.Directory.directory_v1
// Tested with version 1.39.0.1505

// Google.Apis.Admin.Directory.directory_v1.Data.User
// https://developers.google.com/resources/api-libraries/documentation/admin/directory_v1/csharp/latest/classGoogle_1_1Apis_1_1Admin_1_1Directory_1_1directory__v1_1_1Data_1_1User.html

namespace Example
{
    class Program
    {
        static void Main(string[] args)
        {
            // Service Account with Domain-Wide delegation
            var sa_file = "service_account.json";

            // G Suite User to impersonate
            var user_email = "[email protected]";

            // G Suite User to get information about
            var gs_email = "[email protected]";

            // Scopes
            var scopes = "https://www.googleapis.com/auth/admin.directory.user";

            var credential = GoogleCredential.FromFile(sa_file)
                        .CreateScoped(scopes)
                        .CreateWithUser(user_email);

            // Create Directory API service.
            var service = new DirectoryService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential
            });

            try {
                var request = service.Users.Get(gs_email);

                var result = request.Execute();

                Console.WriteLine("Full Name: {0}", result.Name.FullName);
                Console.WriteLine("Email:     {0}", result.PrimaryEmail);
                Console.WriteLine("ID:        {0}", result.Id);
                Console.WriteLine("Is Admin:  {0}", result.IsAdmin);
            } catch {
                Console.WriteLine("User not found.");
            }
        }
    }
}
Stumble answered 7/5, 2019 at 6:38 Comment(2)
I've taken your example and still receive the same message. The user_email is set to our super admin email. The JSON file contains the service account credentials which I got from the console. Not sure where else to look as I've set everything up from what I can seeCheshvan
Error:"invalid_grant", Description:"Invalid email or User ID". This code does not workSteverson
H
4

If you want to use the service account you can authenticate with below code.

String serviceAccountEmail = "yourserviceaccountmail";
public GmailService GetService(string user_email_address)
    {

        var certificate = new X509Certificate2(@"yourkeyfile.p12", 
"notasecret", X509KeyStorageFlags.Exportable);

        ServiceAccountCredential credential = new ServiceAccountCredential(
                   new ServiceAccountCredential.Initializer(serviceAccountEmail)
                   {

                       User = user_email_address,
                       Scopes = new[] { GmailService.Scope.MailGoogleCom }
                   }.FromCertificate(certificate));

        GmailService service = new GmailService(new BaseClientService.Initializer()
        {
            HttpClientInitializer = credential,
            ApplicationName = AppName,
        });

        return service;
    }

You can list users using this service. Its work for me.

And you can list userlist with below code. ( with DirectoryService)

public Users GetDirService()//UserList with DirectoryService
    {
        string Admin_Email = "yoursuperadminemail";
        string domain = "yourdomain.com";
        try
        {
            var certificate = new X509Certificate2(@"yourkeyfile.p12", "notasecret", X509KeyStorageFlags.Exportable);

            ServiceAccountCredential credentialUsers = new ServiceAccountCredential(
            new ServiceAccountCredential.Initializer(serviceAccountEmail)
            {
                Scopes = new[] { DirectoryService.Scope.AdminDirectoryUser },
                User = Admin_Email,
            }.FromCertificate(certificate));

            var serviceUsers = new DirectoryService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credentialUsers,
                ApplicationName = AppName,
            });
            var listReq = serviceUsers.Users.List();
            listReq.Domain = domain;
            Users users = listReq.Execute();
            return users;
        }
        catch (Exception ex)
        {
            MessageBox.Show("your mail address must be super admin authorized.", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
            return null;
        }

    }
Horrendous answered 6/5, 2019 at 10:52 Comment(8)
Can you improve your answer with a complete example?Stumble
I have tested your example and still run into the same error. Feels like i've missed a setting but it all looks correctly setupCheshvan
maybe . I used this code in my project and it is clearly works.Horrendous
@Horrendous Are you able to explain what steps you have taken inside the Google Portal? I have listed in my original post what I have already done. Have you done something I haven't?Cheshvan
@Cheshvan You need delegate domain-wide authority to your service account. Check here and go to Delegate domain-wide authority to your service account step.Horrendous
@Muammer, delegation has already been setup and confirmed via the console stating it's enabledCheshvan
Can you improve your answer by also include the using statements at the top? I am interested in the libraries you are using.Stumble
can this code for service account credentials downloaded as json.Steverson
C
1

Ok I have solved the issue.

Adding the following scope via the security settings within the Google Portal has solved the issue. This is strange as their own example doesn't require this scope to be added ad their documentation doesn't say it's required for this method.

https://www.googleapis.com/auth/admin.directory.group

Cheshvan answered 7/5, 2019 at 16:10 Comment(5)
My example + G Suite do not use the group scope. You have something else going on that is requiring group. Is there something different about this user?Stumble
@JohnHanley - As far as I know, there's nothing special about the super admin or user I'm trying to fetch. We have multiple super admins and switching between them had no effect. Same applies to the user I was fetching.Cheshvan
I am asking about the user in G Suite you are calling "Get()" for. For example, is this user a member of a group? Can you create a new user and then try again? I don't have problems with my code unless I deliberately try to create errors. However, I generally give all scopes so I don't worry about the fine detail of permissions for the delegation as the caller must have Super Admin rights anyways.Stumble
So the user I'm trying to get is associated to groups as well as being a delegated adminCheshvan
I have spent time on this question. I cannot create a configuration in G Suite using groups, permissions, etc that require https://www.googleapis.com/auth/admin.directory.group. You are leaving some detail out that would help identify why group is required. One example where it is required is to List() users.Stumble

© 2022 - 2024 — McMap. All rights reserved.