3

I have created a web application in which I have implemented google sign-in. I want to add search functionality to it, where user can search any file present in his/her drive. In the sample code of Google Drive Api, app opens browser window and after providing credentials, the token is stored locally, and I wanted to pass credentials through code so I created a service account for this.

In google documentation, it is written that Your application now has the authority to make API calls as users in your domain (to "impersonate" users). When you prepare to make authorized API calls, you specify the user to impersonate.

Here is my code-

static void Main(string[] args)
{
    try
    {
        var service = AuthenticateServiceAccountV1(GServiceAccount, "keycredentials.json");
        FilesResource.ListRequest listRequest = service.Files.List();
        listRequest.PageSize = 1000;
        listRequest.Fields = "nextPageToken, files(webViewLink, name, id)";
        IList<Google.Apis.Drive.v3.Data.File> files = listRequest.Execute().Files;
        Console.WriteLine("Processing...\n");
        if (files != null && files.Count > 0)
        {
            var listfiles = files.Select(x => x.Id).ToList();
            Console.WriteLine(files.Count + " records fetched.");
        }
        else
        {
            Console.WriteLine("No files found.");
        }
        Console.ReadLine();
    }
    catch(Exception ex)
    {
        throw ex;
    }
}

static DriveService AuthenticateServiceAccountV1(string ServiceAccountEmail, string KeyFilePath)
{
    try
    {
        if (string.IsNullOrEmpty(KeyFilePath))
            throw new Exception("Path to the service account credentials file is required.");
        if (!File.Exists(KeyFilePath))
            throw new Exception("The service account credentials file does not exist at: " + KeyFilePath);
        if (string.IsNullOrEmpty(ServiceAccountEmail))
            throw new Exception("ServiceAccountEmail is required.");

        if (Path.GetExtension(KeyFilePath).ToLower() == ".json")
        {
            GoogleCredential credential;
            using (var stream = new FileStream(KeyFilePath, FileMode.Open, FileAccess.Read))
            {
                credential = GoogleCredential.FromStream(stream)
                     .CreateScoped(Scopes);
            }

            return new DriveService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = ApplicationName,
            });
        }
        else if (Path.GetExtension(KeyFilePath).ToLower() == ".p12")
        {
            var certificate = new X509Certificate2(KeyFilePath, "notasecret", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
            ServiceAccountCredential credential = new ServiceAccountCredential(
                new ServiceAccountCredential.Initializer(ServiceAccountEmail)
                {
                    Scopes = Scopes,
                }.FromCertificate(certificate));

            return new DriveService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = ApplicationName
            });
        }
        else
        {
            throw new Exception("Unsupported Service accounts credentials.");
        }
    }
    catch (Exception ex)
    {
        return null;
    }
}

In the above code, where can I pass email id of the impersonating user? I am using g-suite accounts for my application. I tried to replace credential = GoogleCredential.FromStream(stream).CreateScoped(Scopes); with credential = GoogleCredential.FromStream(stream).CreateScoped(Scopes).CreateWithUser("userid@gmail.com");

It gives an exception saying

{"Error:\"unauthorized_client\", Description:\"Client is unauthorized to retrieve access tokens using this method.\", Uri:\"\""}

Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449
Sonali
  • 2,223
  • 6
  • 32
  • 69
  • Try changing the order of `CreateScoped` and `CreateWithUser`. I can't explain why but it worked for me. – hultqvist Nov 12 '19 at 20:13

1 Answers1

0

{"Error:\"unauthorized_client\", Description:\"Client is unauthorized to retrieve access tokens using this method.\", Uri:\"\""}

In google developer console you created a project with credentials. There are two types of credential and they have different files and different code needed ot use them.

The code you are using is for authenticating with a service account. The credential files you are using is probably for Oauth2. Go to google developer console and create a different credential type and download the new file.

ServiceAccount.cs

public static DriveService AuthenticateServiceAccount(string serviceAccountEmail, string serviceAccountCredentialFilePath, string[] scopes)
        {
            try
            {
                if (string.IsNullOrEmpty(serviceAccountCredentialFilePath))
                    throw new Exception("Path to the service account credentials file is required.");
                if (!File.Exists(serviceAccountCredentialFilePath))
                    throw new Exception("The service account credentials file does not exist at: " + serviceAccountCredentialFilePath);
                if (string.IsNullOrEmpty(serviceAccountEmail))
                    throw new Exception("ServiceAccountEmail is required.");                

                // For Json file
                if (Path.GetExtension(serviceAccountCredentialFilePath).ToLower() == ".json")
                {
                    GoogleCredential credential;
                    using (var stream = new FileStream(serviceAccountCredentialFilePath, FileMode.Open, FileAccess.Read))
                    {
                        credential = GoogleCredential.FromStream(stream)
                             .CreateScoped(scopes);
                    }

                    // Create the  Analytics service.
                    return new DriveService(new BaseClientService.Initializer()
                    {
                        HttpClientInitializer = credential,
                        ApplicationName = "Drive Service account Authentication Sample",
                    });
                }
                else if (Path.GetExtension(serviceAccountCredentialFilePath).ToLower() == ".p12")
                {   // If its a P12 file

                    var certificate = new X509Certificate2(serviceAccountCredentialFilePath, "notasecret", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
                    var credential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(serviceAccountEmail)
                    {
                        Scopes = scopes
                    }.FromCertificate(certificate));

                    // Create the  Drive service.
                    return new DriveService(new BaseClientService.Initializer()
                    {
                        HttpClientInitializer = credential,
                        ApplicationName = "Drive Authentication Sample",
                    });
                }
                else
                {
                    throw new Exception("Unsupported Service accounts credentials.");
                }

            }
            catch (Exception ex)
            {                
                throw new Exception("CreateServiceAccountDriveFailed", ex);
            }
        }
    }
Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449
  • I didn't get what you are saying `Go to google developer console and create a different credential type and download the new file.` – Sonali Mar 21 '18 at 12:05
  • here https://console.developers.google.com/ this might help as well but the pictures are a little out dated http://www.daimto.com/google-developer-console-service-account/ "keycredentials.json" <--- is the wrong type of credentials – Linda Lawton - DaImTo Mar 21 '18 at 12:09
  • I have actually renamed my file to `keycredentials.json`. – Sonali Mar 21 '18 at 12:39
  • @DalmTo I have followed step provided here https://developers.google.com/identity/protocols/OAuth2ServiceAccount – Sonali Mar 21 '18 at 12:40