How to access contacts API with Service Account - google-contacts-api

I am in the process of upgrading my Marketplace applications to support the new marketplace api and OAUTH 2.
I have managed to migrate most APIs but am stuck on the contacts api. With the the previous marketplace version we used 2LO and client key/client secret to authenticate across the Google Apps domain. My understanding is that the only way to do this in current version is with Service Accounts and OAuth 2.
Based on the V3 calendar API I'm assuming something like this (although the contacts API does not support it from what I can see) -
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { "https://www.google.com/m8/feeds" },
User = "admin#domain.co"
}.FromCertificate(certificate));
var service = new ContactsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Contact API Sample",
});
If anybody has done this, your advice will be appreciated!

I know this question has been answered, but adding an alternative that doesn't require the AssertionFlowClient or a specific version of DotNetOpenAuth. This allows you to use the same ServiceAccountCredential code from your original question (ie, using the same method to query a user's gmail)
Thank you for following up on your own post, it helped me find this solution!
private static ServiceAccountCredential GenerateCred(IEnumerable<string> scopes, string delegationUser)
{
var certificate = new X509Certificate2(certLocation, certPassword, X509KeyStorageFlags.Exportable);
var credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = scopes,
User = delegationUser
}.FromCertificate(certificate));
return credential;
}
var credential = GenerateCred(new[] { "https://www.googleapis.com/auth/contacts.readonly" }, userToDelegate);
//Get the token for this scope and user
await credential.RequestAccessTokenAsync(new CancellationToken());
//use the token to initalize the request
var rs = new RequestSettings(projectName)
{
OAuth2Parameters = new OAuth2Parameters()
{
AccessToken = credential.Token.AccessToken
}
};
var request = new ContactsRequest(rs);
Feed<Contact> f = request.GetContacts();
foreach (var c in f.Entries)
{
//process each contact
}

Well I got no response anywhere on this so I can only assume that the standard client libraries don't have support for this.
I have come up with a workaround based on the following post for email access.
http://www.limilabs.com/blog/oauth2-gmail-imap-service-account
You will need to follow his post and note the following:
this must use the DotNetOpenAuth version that he specifies. The latest version will not work.
You will only need his AssertionFlowClient classes which can be added to the below code I have provided here:
X509Certificate2 certificate = new X509Certificate2(
serviceAccountCertPath,
serviceAccountCertPassword,
X509KeyStorageFlags.Exportable);
AuthorizationServerDescription server = new AuthorizationServerDescription
{
AuthorizationEndpoint = new Uri("https://accounts.google.com/o/oauth2/auth"),
TokenEndpoint = new Uri("https://accounts.google.com/o/oauth2/token"),
ProtocolVersion = ProtocolVersion.V20,
};
AssertionFlowClient provider = new AssertionFlowClient(server, certificate)
{
ServiceAccountId = serviceAccountEmail,
Scope = string.Join(" ", new[] { "https://mail.google.com/", "https://www.google.com/m8/feeds/" }),
ServiceAccountUser = userEmail,
};
IAuthorizationState grantedAccess = AssertionFlowClient.GetState(provider);
RequestSettings rs = new RequestSettings("iLink");
rs.OAuth2Parameters = new OAuth2Parameters();
rs.OAuth2Parameters.AccessToken = grantedAccess.AccessToken;
rs.OAuth2Parameters.RefreshToken = null;
rs.OAuth2Parameters.ClientId = null;
rs.OAuth2Parameters.ClientSecret = null;
rs.OAuth2Parameters.RedirectUri = null;
ContactsRequest cr = new ContactsRequest(rs);
Feed<Contact> f = cr.GetContacts();
foreach (var c in f.Entries)
{
Response.Write(c.Name.FullName);
}

Related

Getting service account credentials Apps Script - TypeError: Cannot read property from null

I am developing a community connector for Data Studio, directly linked to BigQuery, using Data Studio Advanced Services. However, I am not able to retrieve the service account credentials.
I have already copied and pasted the whole json file for the service account key as a string in SERVICE_ACCOUNT_CREDS var, as well as the whole string in the 'private_key' in the json in the SERVICE_ACCOUNT_KEY var, as well as the service account e-mail and billing project id as strings, respectively in the vars SERVICE_ACCOUNT_EMAIL and BILLING_PROJECT_ID. It fails when I try to authenticate when I run the getData() function.
Code (is the World Bank advanced services example, but with my credentials):
var cc = DataStudioApp.createCommunityConnector();
var scriptProperties = PropertiesService.getScriptProperties();
function isAdminUser() {
return false;
}
function getAuthType() {
return cc
.newAuthTypeResponse()
.setAuthType(cc.AuthType.NONE)
.build();
}
function getConfig(request) {
var config = cc.getConfig();
config
.newInfo()
.setId('info')
.setText(
'No configuration is required for this connector. Click connect to create a new data source.'
);
return config.build();
}
function getFields() {
var fields = cc.getFields();
var types = cc.FieldType;
var aggregations = cc.AggregationType;
fields
.newDimension()
.setId('country_name')
.setName('Country')
.setType(types.TEXT);
fields
.newDimension()
.setId('country_code')
.setName('Country Code')
.setType(types.TEXT);
fields
.newDimension()
.setId('indicator_name')
.setName('Indicator')
.setType(types.TEXT);
fields
.newDimension()
.setId('year')
.setName('Year')
.setType(types.YEAR);
fields
.newMetric()
.setId('value')
.setName('Value')
.setType(types.NUMBER)
.setIsReaggregatable(true)
.setAggregation(aggregations.SUM);
return fields;
}
function getSchema(request) {
return {
schema: getFields().build()
};
}
var SERVICE_ACCOUNT_CREDS = '{'+
'"type": "service_account",'+
'"project_id": "string for the project id",'+
'"private_key_id": "string for the private key id",'+
'"private_key": "-----BEGIN PRIVATE KEY-----the private key-----END PRIVATE KEY-----\n",'+
'"client_email": "serviceaccount#projectid.iam.gserviceaccount.com",'+
'"client_id": "the client id",'+
'"auth_uri": "https://accounts.google.com/o/oauth2/auth",'+
'"token_uri": "https://oauth2.googleapis.com/token",'+
'"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",'+
'"client_x509_cert_url": "url"'+
'}';
var SERVICE_ACCOUNT_KEY = 'private key string';
var SERVICE_ACCOUNT_EMAIL = 'serviceaccount#projectid.iam.gserviceaccount.com';
var BILLING_PROJECT_ID = 'projectid';
/**
* Copy the entire credentials JSON file from creating a service account in GCP.
*/
function getServiceAccountCreds() {
return JSON.parse(scriptProperties.getProperty(SERVICE_ACCOUNT_CREDS));
}
function getOauthService() {
var serviceAccountCreds = getServiceAccountCreds();
var serviceAccountKey = serviceAccountCreds[SERVICE_ACCOUNT_KEY];
var serviceAccountEmail = serviceAccountCreds[SERVICE_ACCOUNT_EMAIL];
return OAuth2.createService('WorldBankHealthPopulation')
.setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
.setPrivateKey(serviceAccountKey)
.setIssuer(serviceAccountEmail)
.setPropertyStore(scriptProperties)
.setCache(CacheService.getScriptCache())
.setScope(['https://www.googleapis.com/auth/bigquery.readonly']);
}
var FIELDS_WHITELIST = [
'country_name',
'country_code',
'indicator_name',
'year',
'value'
];
var BASE_SQL =
'SELECT {{FIELDS}} FROM `bigquery-public-data.world_bank_health_population.health_nutrition_population`';
function makeSQL(request) {
// Create an object of {[fieldName]: boolean} to use as a constant time lookup.
var simpleSet = FIELDS_WHITELIST.reduce(function(obj, field) {
obj[field] = true;
return obj;
}, {});
var requestFieldNames = request.fields.map(function(field) {
return field.name;
});
var fieldNames = FIELDS_WHITELIST.filter(function(fieldName) {
return simpleSet[fieldName];
});
var fieldsSQL = fieldNames.join(', ');
return BASE_SQL.replace('{{FIELDS}}', fieldsSQL);
}
function getData(request) {
var accessToken = getOauthService().getAccessToken();
var serviceAccountCreds = getServiceAccountCreds();
var billingProjectId = serviceAccountCreds[BILLING_PROJECT_ID];
var sql = makeSQL(request);
return cc
.newBigQueryConfig()
.setAccessToken(accessToken)
.setBillingProjectId(billingProjectId)
.setUseStandardSql(true)
.setQuery(sql)
.build();
}
Whenever I run either getOauthService() or getData() I get 'Cannot read property 'private key string' from null
Any help or tutorial fit for a 5-year old is highly appreciated.
Answer
In your getServiceAccountCreds function you are retrieving a field by using an object scriptProperties.getProperty(SERVICE_ACCOUNT_CREDS) which is SERVICE_ACCOUNT_CREDS. As per the Google Apps Script: Properties example, the only type of parameter expected is a String. For that reason, you should avoid parsing the JSON and use the Properties object created at the beginning of the code.
I recommend you to use the following code.
Code
Replace your code
var SERVICE_ACCOUNT_CREDS = '{'+
'"type": "service_account",'+
'"project_id": "string for the project id",'+
'"private_key_id": "string for the private key id",'+
'"private_key": "-----BEGIN PRIVATE KEY-----the private key-----END PRIVATE KEY-----\n",'+
'"client_email": "serviceaccount#projectid.iam.gserviceaccount.com",'+
'"client_id": "the client id",'+
'"auth_uri": "https://accounts.google.com/o/oauth2/auth",'+
'"token_uri": "https://oauth2.googleapis.com/token",'+
'"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",'+
'"client_x509_cert_url": "url"'+
'}';
var SERVICE_ACCOUNT_KEY = 'private key string';
var SERVICE_ACCOUNT_EMAIL = 'serviceaccount#projectid.iam.gserviceaccount.com';
var BILLING_PROJECT_ID = 'projectid';
/**
* Copy the entire credentials JSON file from creating a service account in GCP.
*/
function getServiceAccountCreds() {
return JSON.parse(scriptProperties.getProperty(SERVICE_ACCOUNT_CREDS));
}
For this code
var SERVICE_ACCOUNT_CREDS_PROPS = {
type: "service_account",
project_id: "string for the project id",
private_key_id: "string for the private key id",
private_key: "-----BEGIN PRIVATE KEY-----the private key-----END PRIVATE KEY-----\n",
client_email: "serviceaccount#projectid.iam.gserviceaccount.com",
client_id: "the client id",
auth_uri: "https://accounts.google.com/o/oauth2/auth",
token_uri: "https://oauth2.googleapis.com/token",
auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs",
client_x509_cert_url: "url"
};
scriptProperties.setProperties(SERVICE_ACCOUNT_CREDS_PROPS);
var SERVICE_ACCOUNT_KEY = 'private_key';
var SERVICE_ACCOUNT_EMAIL = 'client_email';
var BILLING_PROJECT_ID = 'project_id';
/**
* Copy the entire credentials JSON file from creating a service account in GCP.
*/
function getServiceAccountCreds() {
return scriptProperties.getProperties();
}
Reference
Google Apps Script: PropertiesService
Google Apps Script: Properties
Actually, altough the connector code ran in apps script after #Jose Vasquez suggestion, per using it on Google Data Studio returned an error Insufficient permissions to the underlying data set.. Upon further evaluation of the tutorial, I missed a very important point. The json corresponding to the credentials should not be pasted in the code itself, but rather referenced in the project properties:
And then on the Script Properties tab, as a key-value pair, the key being SERVICE_ACCOUNT_CREDS and the value being the content of the json service account key:
Then, the code replacement becomes from:
var SERVICE_ACCOUNT_CREDS = '{'+
'"type": "service_account",'+
'"project_id": "string for the project id",'+
'"private_key_id": "string for the private key id",'+
'"private_key": "-----BEGIN PRIVATE KEY-----the private key-----END PRIVATE KEY-----\n",'+
'"client_email": "serviceaccount#projectid.iam.gserviceaccount.com",'+
'"client_id": "the client id",'+
'"auth_uri": "https://accounts.google.com/o/oauth2/auth",'+
'"token_uri": "https://oauth2.googleapis.com/token",'+
'"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",'+
'"client_x509_cert_url": "url"'+
'}';
var SERVICE_ACCOUNT_KEY = 'private key string';
var SERVICE_ACCOUNT_EMAIL = 'serviceaccount#projectid.iam.gserviceaccount.com';
var BILLING_PROJECT_ID = 'string for the project id';
/**
* Copy the entire credentials JSON file from creating a service account in GCP.
*/
function getServiceAccountCreds() {
return JSON.parse(scriptProperties.getProperty(SERVICE_ACCOUNT_CREDS));
}
to simply:
var SERVICE_ACCOUNT_CREDS = 'SERVICE_ACCOUNT_CREDS';
var SERVICE_ACCOUNT_KEY = 'private_key';
var SERVICE_ACCOUNT_EMAIL = 'client_email';
var BILLING_PROJECT_ID = 'project_id';
/**
* Copy the entire credentials JSON file from creating a service account in GCP.
*/
function getServiceAccountCreds() {
return JSON.parse(scriptProperties.getProperty(SERVICE_ACCOUNT_CREDS));
}
Since the service account credentials are already set in the script properties

FileStream - Path found in console-application but not in mvc

Iv'e used this guide drive quickstart and successfully got it worked in a console-app.
Now I'm trying to do the same thing in an mvc-application, but I got error when creating a new FileStream.
The code a use is exactly the same in mvc instead this time i'ts triggered by a button-click. This is my code:
using (var stream = new FileStream("client_secret.json", FileMode.Open, FileAccess.Read))
{
string credPath = System.Environment.GetFolderPath(
System.Environment.SpecialFolder.Personal);
credPath = Path.Combine(credPath, ".credentials/drive-dotnet-quickstart.json");
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
// Create Drive API service.
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
// Define parameters of request.
FilesResource.ListRequest listRequest = service.Files.List();
listRequest.PageSize = 10;
listRequest.Fields = "nextPageToken, files(id, name)";
// List files.
IList<Google.Apis.Drive.v3.Data.File> files = listRequest.Execute()
.Files;
I got error at 'using (var stream = new FileStream("client_secret.json", FileMode.Open, FileAccess.Read))'
My 'client_secret.json'-file is stored at the root of my mvc project.
And I've done the step: Select client_secret.json, and then go to the Properties window and set the Copy to Output Directory field to Copy always.
Why is this not working in my mvc-app?
The error I get is 'System.IO.FileNotFoundException' - '{"Cannot find file c:\windows\system32\inetsrv\client_secret.json.":"c:\windows\system32\inetsrv\client_secret.json"}'

gsutil on a Google Compute Engine VM can't use service account authentication with a key file

I'm launching an instance from the google .net API and despite my best efforts I can't get it to copy anything to or from storage. Currently I'm authenticating with a developer console service account like this:-
string ServiceAccountEmail = "blahblah#developer.gserviceaccount.com";
var certificate = new X509Certificate2(#"key.p12", "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(ServiceAccountEmail)
{
Scopes = new[] { ComputeService.Scope.Compute, ComputeService.Scope.DevstorageFullControl }
}.FromCertificate(certificate));
var cs = new ComputeService(new BaseClientService.Initializer
{
ApplicationName = "appname",
HttpClientInitializer = (Google.Apis.Http.IConfigurableHttpClientInitializer)credential,
});
Google.Apis.Compute.v1.Data.Instance newinst = new Google.Apis.Compute.v1.Data.Instance();
newinst.Name = "generatedinstance";
newinst.MachineType = "https://www.googleapis.com/compute/v1/projects/projectid/zones/zone/machineTypes/n1-standard-1";
Google.Apis.Compute.v1.Data.AttachedDisk ad = new Google.Apis.Compute.v1.Data.AttachedDisk();
ad.AutoDelete = true;
ad.Boot = true;
ad.Type = "PERSISTENT";
ad.InitializeParams = new Google.Apis.Compute.v1.Data.AttachedDiskInitializeParams();
ad.InitializeParams.DiskName = "newdisk";
ad.InitializeParams.SourceImage = "https://www.googleapis.com/compute/v1/projects/projectid/global/images/customimage";
ad.InitializeParams.DiskType = "https://www.googleapis.com/compute/v1/projects/projectid/zones/zone/diskTypes/pd-standard";
ad.Mode = "READ_WRITE";
newinst.Disks = new List<Google.Apis.Compute.v1.Data.AttachedDisk>();
newinst.Disks.Add(ad);
Google.Apis.Compute.v1.Data.NetworkInterface ni = new Google.Apis.Compute.v1.Data.NetworkInterface();
ni.Network = "https://www.googleapis.com/compute/v1/projects/projectid/global/networks/default";
ni.AccessConfigs = new List<Google.Apis.Compute.v1.Data.AccessConfig>();
ni.AccessConfigs.Add(new Google.Apis.Compute.v1.Data.AccessConfig
{
Type = "ONE_TO_ONE_NAT",
Name = "External NAT",
});
newinst.NetworkInterfaces = new List<Google.Apis.Compute.v1.Data.NetworkInterface>();
newinst.NetworkInterfaces.Add(ni);
var start = new Google.Apis.Compute.v1.Data.Metadata.ItemsData();
start.Key = "startup-script";
start.Value = "*startup script* includes gsutil cp which won't work without service account attached";
newinst.Metadata = new Google.Apis.Compute.v1.Data.Metadata();
newinst.Metadata.Kind = "compute#metadata";
newinst.Metadata.Items = new List<Google.Apis.Compute.v1.Data.Metadata.ItemsData>();
newinst.Metadata.Items.Add(start);
newinst.ServiceAccounts = new List<Google.Apis.Compute.v1.Data.ServiceAccount>();
//var sa = new Google.Apis.Compute.v1.Data.ServiceAccount();|with this section
//sa.Email = "blahblah#developer.gserviceaccount.com"; |the instance won't
//sa.Scopes = new[] { ComputeService.Scope.Compute, |start. (An equivalent
ComputeService.Scope.DevstorageFullControl }; |is found in instance
//newinst.ServiceAccounts.Add(sa); |start REST request)
var instinsert = new InstancesResource.InsertRequest(cs, newinst, "projectid", "zone");
var insertresponse = instinsert.Execute();
The message I get when I try to use gsutil cp is "You do not currently have an active account selected.". Can anyone tell me where I'm going wrong?
You need to run gcloud auth activate-service-account blahblah#developer.gserviceaccount.com --key-file path_to_key.p12 to tell the Cloud SDK (including gsutil) about your service account.
As per the code provided, I can see that the original example has
var certificate = new X509Certificate2(#"key.p12", "notasecret", X509KeyStorageFlags.Exportable);
I notice you are missing the '#'in your code. I'm not very familiar with .Net. I have tested these examples in python and this one. When creating my instance I added the service account for GCS and the file is uploaded correctly.
OKAY! Problem solved. The part I was getting wrong was the bit commented out in the question-
var sa = new Google.Apis.Compute.v1.Data.ServiceAccount();
sa.Email = "blahblah#developer.gserviceaccount.com";
sa.Scopes = new[] { ComputeService.Scope.Compute,
ComputeService.Scope.DevstorageFullControl };
newinst.ServiceAccounts.Add(sa);
I needed the email for the main service account for the developer console in this section rather than the same service account I used to create the credentials but don't ask me why. Point is the instance launches and gsutil is now happily copying away.
Thanks for your time and help everyone!
Ross

where are my upload to google drive's files for service account

I have the code below that uploads a file to my Google service account. It is working, but I want to know where is the file? Which account can I use to login to google drive and get the file and how much space can I use? Is there anything like google drive UI for service accounts?
private static Google.Apis.Drive.v2.Data.File insertFile(
String title,
String mimeType,
MemoryStream FileStream)
{
String serviceAccountEmail = "XXXXXX#developer.gserviceaccount.com";
var certificate = new X509Certificate2(
#"XXXXXXprivatekey.p12",
"notasecret",
X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { DriveService.Scope.Drive }
}.FromCertificate(certificate));
// Create the service.
DriveService service = new DriveService(
new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Drive API Sample",
});
// File's metadata.
Google.Apis.Drive.v2.Data.File body =
new Google.Apis.Drive.v2.Data.File();
body.Title = title;
body.Description = title;
body.MimeType = mimeType;
FilesResource.InsertMediaUpload request =
service.Files.Insert(body, FileStream, mimeType);
request.Upload();
Google.Apis.Drive.v2.Data.File file = request.ResponseBody;
return file;
}
There is no UI for Service Accounts. The files are only accessible to your app.

AssertionFlowClient depreceated, trying to use ServiceAccountCredential, but it won't work

I've tried creating a DriveService using service account acting on behalf of another user.
I've copied this code from google documentation found here https://developers.google.com/drive/delegation
static DriveService BuildService() {
X509Certificate2 certificate = new X509Certificate2(SERVICE_ACCOUNT_PKCS12_FILE_PATH, "notasecret", X509KeyStorageFlags.Exportable);
var provider = new AssertionFlowClient(GoogleAuthenticationServer.Description, certificate)
{
ServiceAccountId = SERVICE_ACCOUNT_EMAIL,
Scope = DriveService.Scopes.Drive.GetStringValue(),
};
var auth = new OAuth2Authenticator<AssertionFlowClient>(provider, AssertionFlowClient.GetState);
return new DriveService(auth);
}
but I get this warning when trying to build the project:
Warning 4 'Google.Apis.Authentication.OAuth2.DotNetOpenAuth.AssertionFlowClient' is obsolete: 'AssertionFlowClient is not supported any more and it's going to be removed in 1.7.0-beta. Consider using ServiceAccountCredential from the new Google.Apis.Auth NuGet package.'
and I also get this error:
Error 11 Argument 1: cannot convert from 'Google.Apis.Authentication.OAuth2.OAuth2Authenticator' to 'Google.Apis.Services.BaseClientService.Initializer'
Then I googled ServiceAccountCredential and ended up with this code (derived from this page: https://code.google.com/p/google-api-dotnet-client/wiki/OAuth2#Service_Accounts)
static DriveService BuildService() {
X509Certificate2 certificate = new X509Certificate2(SERVICE_ACCOUNT_PKCS12_FILE_PATH, "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(SERVICE_ACCOUNT_EMAIL)
{
User = "someone#mydomain.mygbiz.com",
Scopes = new[] { DriveService.Scope.DriveFile }
}.FromCertificate(certificate));
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Drive API Sample",
});
return service;
}
When I try to build this code it seems all fine, but when I run it I get the following error.
A first chance exception of type 'System.Security.Cryptography.CryptographicException' occurred in mscorlib.dll
Additional information: Det går inte att hitta det begärda objektet.
(Translated : The requested object cannot be found)
If there is a handler for this exception, the program may be safely continued.
The error occurs on this line:
X509Certificate2 certificate = new X509Certificate2(SERVICE_ACCOUNT_PKCS12_FILE_PATH, "notasecret", X509KeyStorageFlags.Exportable);
Anyone have any ideas?
Update 2013 Oct 31
I have tried this code:
{
Console.WriteLine("Drive API - Service Account");
Console.WriteLine("==========================");
String serviceAccountEmail = "<some email>#developer.gserviceaccount.com";
var certificate = new X509Certificate2(#"key.p12", "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
User = "<someuser>#<mydomain>.mygbiz.com",
Scopes = new[] { DriveService.Scope.Drive }
}.FromCertificate(certificate));
// Create the service.
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "DrvMgr",
});
Console.WriteLine("Executing listing");
FileList UserFiles = service.Files.List().Execute();
I get this error message:
An unhandled exception of type 'Google.Apis.Auth.OAuth2.Responses.TokenResponseException' occurred in Google.Apis.dll
Additional information: Error:"access_denied", Description:"", Uri:""
It looks like the path to your p12 file is incorrect. See the Plus.ServiceAccount sample for a working solution.
I think that in this sample, the key.p12 was added to the project as a content file which is always copied to the output directory. Then mentioning "key.p12" file path from the code will result in grabbing this file from the output folder.