Using Google API to modify google contacts - google-api

I want to start developing with Google API’s with a .NET client. For first step I tried to get all google contacts and now I want to insert contacts.
I have read a lot about Google API’s to CRUD (create, read, update and delete) contacts. There are the Contact API, the People API and other(?). What is the best way to CRUD contacts?
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = "xyz.apps.googleusercontent.com",
ClientSecret = " xyz"
},
new[] { "profile", "https://www.google.com/m8/feeds/contacts/xy%40gmail.com/full" },
"me",
CancellationToken.None).Result;
// Create the service.
var peopleService = new PeopleService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "WindowsClient-Google-Sync",
});
ListRequest listRequest = peopleService.People.Connections.List("people/me");
listRequest.SyncToken = null;
ListConnectionsResponse result = listRequest.Execute();
foreach (Person person in result.Connections)
{
foreach (Name name in person.Names)
{
Console.WriteLine("Name: " + name.DisplayName);
}
}
How can I extend this sample to create or update contacts?
Thanks
Andreas

If you will check Google Contacts API:
The Google Contacts API allows client applications to view and update a user's contacts. Contacts are stored in the user's Google Account; most Google services have access to the contact list.
Your client application can use the Google Contacts API to create new contacts, edit or delete existing contacts, and query for contacts that match particular criteria
Creating Contact
To create a new contact, send an authorized POST request to the user's contacts feed URL with contact data in the body.
The URL is of the form:
https://www.google.com/m8/feeds/contacts/{userEmail}/full
Upon success, the server responds with an HTTP 201 Created status code and the created contact entry with some additional elements and properties (shown in bold) that are set by the server, such as id, various link elements and properties.
import com.google.gdata.client.contacts.ContactsService;
import com.google.gdata.data.contacts.ContactEntry;
import com.google.gdata.data.contacts.ContactGroupFeed;
import com.google.gdata.data.extensions.City;
import com.google.gdata.data.extensions.Country;
import com.google.gdata.data.extensions.Email;
import com.google.gdata.data.extensions.ExtendedProperty;
import com.google.gdata.data.extensions.FormattedAddress;
import com.google.gdata.data.extensions.FullName;
import com.google.gdata.data.extensions.Im;
import com.google.gdata.data.extensions.Name;
import com.google.gdata.data.extensions.PhoneNumber;
import com.google.gdata.data.extensions.PostCode;
import com.google.gdata.data.extensions.Region;
import com.google.gdata.data.extensions.Street;
import com.google.gdata.data.extensions.StructuredPostalAddress;
// ...
public static ContactEntry createContact(ContactsService myService) {
// Create the entry to insert.
ContactEntry contact = new ContactEntry();
// Set the contact's name.
Name name = new Name();
final String NO_YOMI = null;
name.setFullName(new FullName("Elizabeth Bennet", NO_YOMI));
name.setGivenName(new GivenName("Elizabeth", NO_YOMI));
name.setFamilyName(new FamilyName("Bennet", NO_YOMI))
contact.setName(name);
contact.setContent(new PlainTextConstruct("Notes"));
// Set contact's e-mail addresses.
Email primaryMail = new Email();
primaryMail.setAddress("liz#gmail.com");
primaryMail.setDisplayName("E. Bennet");
primaryMail.setRel("http://schemas.google.com/g/2005#home");
primaryMail.setPrimary(true);
contact.addEmailAddress(primaryMail);
Email secondaryMail = new Email();
secondaryMail.setAddress("liz#example.com");
secondaryMail.setRel("http://schemas.google.com/g/2005#work");
secondaryMail.setPrimary(false);
contact.addEmailAddress(secondaryMail);
// Set contact's phone numbers.
PhoneNumber primaryPhoneNumber = new PhoneNumber();
primaryPhoneNumber.setPhoneNumber("(206)555-1212");
primaryPhoneNumber.setRel("http://schemas.google.com/g/2005#work");
primaryPhoneNumber.setPrimary(true);
contact.addPhoneNumber(primaryPhoneNumber);
PhoneNumber secondaryPhoneNumber = new PhoneNumber();
secondaryPhoneNumber.setPhoneNumber("(206)555-1213");
secondaryPhoneNumber.setRel("http://schemas.google.com/g/2005#home");
contact.addPhoneNumber(secondaryPhoneNumber);
// Set contact's IM information.
Im imAddress = new Im();
imAddress.setAddress("liz#gmail.com");
imAddress.setRel("http://schemas.google.com/g/2005#home");
imAddress.setProtocol("http://schemas.google.com/g/2005#GOOGLE_TALK");
imAddress.setPrimary(true);
contact.addImAddress(imAddress);
// Set contact's postal address.
StructuredPostalAddress postalAddress = new StructuredPostalAddress();
postalAddress.setStreet(new Street("1600 Amphitheatre Pkwy"));
postalAddress.setCity(new City("Mountain View"));
postalAddress.setRegion(new Region("CA"));
postalAddress.setPostcode(new PostCode("94043"));
postalAddress.setCountry(new Country("US", "United States"));
postalAddress.setFormattedAddress(new FormattedAddress("1600 Amphitheatre Pkwy Mountain View"));
postalAddress.setRel("http://schemas.google.com/g/2005#work");
postalAddress.setPrimary(true);
contactOne.addStructuredPostalAddress(postalAddress);
// Ask the service to insert the new entry
URL postUrl = new URL("https://www.google.com/m8/feeds/contacts/default/full");
ContactEntry createdContact = myService.insert(postUrl, contact);
System.out.println("Contact's ID: " + createdContact.getId());
return createdContact;
}
Update Contact
To update a contact, first retrieve the contact entry, modify the data and send an authorized PUT request to the contact's edit URL with the modified contact entry in the body.
The URL is of the form:
https://www.google.com/m8/feeds/contacts/userEmail/full/{contactId}
To ensure that the data sent to the API doesn't overwrite another client's changes, the contact entry's Etag should be provided in the request header.
If-Match: Etag
Upon success, the server responds with an HTTP 200 OK status code and the updated contact entry.
public static ContactEntry updateContactName(
ContactsService myService, URL contactURL)
throws ServiceException, IOException {
// First retrieve the contact to updated.
ContactEntry entryToUpdate = myService.getEntry(contactURL, ContactEntry.class);
entryToUpdate.getName().getFullName().setValue("New Name");
entryToUpdate.getName().getGivenName().setValue("New");
entryToUpdate.getName().getFamilyName().setValue("Name");
URL editUrl = new URL(entryToUpdate.getEditLink().getHref());
try {
ContactEntry contactEntry = myService.update(editUrl, entryToUpdate);
System.out.println("Updated: " + contactEntry.getUpdated().toString());
return contactEntry;
} catch (PreconditionFailedException e) {
// Etags mismatch: handle the exception.
}
return null;
}
Delete Contact
To delete a contact, send an authorized DELETE request to the contact's edit URL.
The URL is of the form:
https://www.google.com/m8/feeds/contacts/{userEmail}/full/{contactId}
To ensure that the data sent to the API doesn't overwrite another client's changes, the contact entry's Etag should be provided in the request header.
If-Match: Etag
Upon success, the server responds with an HTTP 200 OK status code.
public static void deleteContact(ContactsService myService, URL contactURL)
throws ServiceException, IOException {
// Retrieving the contact is required in order to get the Etag.
ContactEntry contact = myService.getEntry(contactURL, ContactEntry.class);
try {
contact.delete();
} catch (PreconditionFailedException e) {
// Etags mismatch: handle the exception.
}
}
while People API:
The People API lets you list authenticated users' Contacts and retrieve profile information for authenticated users and their contacts.
For example, let's say the authenticated user, Jen, has Fabian and Ranjith in her private contacts. When your app calls people.connections.list to retrieve a list of her connections, Jen is presented with a consent screen asking to give the app access to the list. If Jen consents, the app retrieves a list containing Fabian and Ranjith (with a resource name for each person). The app can then call people.get, passing in a resource name, to get private contact and public profile data for each person.

Related

Unable to add attendees to google calendar using service account

I'm having couple of issues with adding attendees to my calendar using google calendar API and service account.
My service account has domain-wide delegation ticket in the cloud console and I am able to create/update a event without adding attendees.
When I try to send request with attendee then I get following response:
Google.Apis.Requests.RequestError
Service accounts cannot invite attendees without Domain-Wide Delegation of Authority. [403]
Errors [Message[Service accounts cannot invite attendees without Domain-Wide Delegation of Authority.] Location[ - ] Reason[forbiddenForServiceAccounts] Domain[calendar]
This is how my code looks like:
var eventDetails = _calendarService.GetEvent(eventDto.EventId).GetAwaiter().GetResult();
eventDetails.Attendees.Add( new EventAttendee
{
DisplayName = "Test Name",
Email = "testemail#example.com" // I tried with few emails but results is same
});
_credential = AuthorizeServiceAccount();
_calendarService = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = _credential,
ApplicationName = _calendarOptions.AplicationName,
});
var request = _calendarService.Events.Patch(eventRequest,_calendarOptions.CalendarId, eventId);
var result = await request.ExecuteAsync();
public GoogleCredential AuthorizeServiceAccount()
{
var serviceAccountCredential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(_calendarOptions.Id) // I'm using service account email here, tried with clientId but same result
{
ProjectId = _calendarOptions.ProjectId,
KeyId = _calendarOptions.KeyId,
User = _calendarOptions.User, // I tried with email from request or null value - for both same result
Scopes = _calendarOptions.Scopes, // ["https://www.googleapis.com/auth/gmail.send", "https://www.googleapis.com/auth/calendar.readonly","https://www.googleapis.com/auth/calendar.events","https://www.googleapis.com/auth/calendar" ],
}
.FromPrivateKey(_calendarOptions.Key));
return GoogleCredential.FromServiceAccountCredential(serviceAccountCredential);
}
Can you please help me so I will be able to add attendees to my events? I don't need to send emails to attendees but emails are required in google API. I don't want also to have G Suite domain account as it's paid.

When I connect to an Account using the Drive API, I get an email with Appname of 'QuickStart'

I have recently setup an account in Google APIs and Services. In this account I enabled Google Drive integration. In the Drive UI Integration section, I have setup the Application Name as per this screenshot: https://www.screencast.com/t/5VphcOkfAXyN
In the Credentials section which I setup for this application, I also entered my application name: https://www.screencast.com/t/3XudlhbE
When I connect to my account via OpenAuth, I get a confirmation email to the connected account, from Google, which tells me that 'Quickstart connected to your Google Account'. I am expecting that the email would tell me 'My App Name connected to your Google Account': https://www.screencast.com/t/fmSXswXS
I have searched through my Google Account settings but I can't find anywhere where it says 'Quickstart'. What do I need to do in order to ensure that my actual app name is the one that appears on the confirmation email, and in the 'Apps with access to your account' permissions page (https://www.screencast.com/t/hcHSkDr5)?
Here is my FlowMetadata as requested in the comments:
public class GoogleDriveAppFlowMetadata : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = AppConfig.CloudStorage.Google.Drive.ClientId,
ClientSecret = AppConfig.CloudStorage.Google.Drive.ClientSecret,
},
Scopes = new[] { DriveService.Scope.Drive },
DataStore = new GoogleDriveDataStore(),
});
public override string AuthCallback
{
get { return #"/GoogleDriveAuthCallback/IndexAsync"; }
}
public override string GetUserId(Controller controller)
{
if (controller.HttpContext.Session[SessionConstants.CloudStorageAccount] == null)
throw new Exception("CloudStorageAccount was not populated!");
CloudStorageDto cs = (CloudStorageDto)controller.HttpContext.Session[SessionConstants.CloudStorageAccount];
return $"{controller.User.GetSessionToken()}_{cs.Id}";
}
public override IAuthorizationCodeFlow Flow
{
get { return flow; }
}
}
I found the setting in the OAuth Consent Screen tab - this is the one that I needed: https://screencast.com/t/cI4lZKmNWeKp

401 Access denied. You are not authorized to read activity records

I'm trying to get data from Reports API.
I get access token for service account and using it in GET request. Response always
{
"error": {
"errors": [
{
"domain": "global",
"reason": "authError",
"message": "Access denied. You are not authorized to read activity records.",
"locationType": "header",
"location": "Authorization"
}
],
"code": 401,
"message": "Access denied. You are not authorized to read activity records."
}
}
I'm using Java for request. Without Google API library (client requirement). Source code is
String urlString = "https://www.googleapis.com/admin/reports/v1/activity/users/all/applications/drive?maxResults=25";
URL url = new URL(urlString);
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
// optional default is GET
urlConnection.setRequestMethod("GET");
urlConnection.setDoInput(true);
// Add request header.
urlConnection.setRequestProperty("Authorization", "Bearer " + accessToken.getValue());
int responseCode = urlConnection.getResponseCode();
System.out.println("\nSending 'GET' request to URL : " + urlString);
System.out.println("Response Code : " + responseCode);
BufferedReader bufferedReader;
if (responseCode == 200) {
bufferedReader = new BufferedReader(
new InputStreamReader(urlConnection.getInputStream()));
} else {
bufferedReader = new BufferedReader(
new InputStreamReader(urlConnection.getErrorStream()));
}
String inputLine;
StringBuffer stringBuffer = new StringBuffer();
while ((inputLine = bufferedReader.readLine()) != null) {
stringBuffer.append(inputLine);
}
bufferedReader.close();
System.out.println(stringBuffer.toString());
Can you, please, help me what I'm missing?
Regards,
Aleks.
"Access denied. You are not authorized to read activity records.",
means just that the user you are authecated with does not have access to do what you are trying to do. To use a service account with this api you need to set up domain wide delegation
In enterprise applications you may want to programmatically access a user's data without any manual authorization on their part. In G Suite domains, the domain administrator can grant third-party applications with domain-wide access to its users' data — this is referred as domain-wide delegation of authority. To delegate authority this way, domain administrators can use service accounts with OAuth 2.0.
Go to your G Suite domain’s Admin console.
Select Security from the list of controls. If you don't see Security listed, select More controls from the gray bar at the bottom of the page, then select Security from the list of controls.
Select Advanced settings from the list of options.
Select Manage third party OAuth Client access in the Authentication section.
In the Client name field enter the service account's Client ID.
In the One or More API Scopes field enter the list of scopes that your application should be granted access to (see image below). For example if you need domain-wide access to activity reports enter: https://www.googleapis.com/auth/admin.reports.audit.readonly
Click the Authorize button.
Just the below code. Two things are very important here: email-id i.e. SERVICE_ACCOUNT_EMAIL and json file SERVICE_ACCOUNT_PKCS12_FILE_PATH:
Source: https://developers.google.com/admin-sdk/reports/v1/guides/delegation
I am using the the GO version of it and it works like a charm after spending 2 days on it :)) (by the way GO version can be found here: https://developers.google.com/admin-sdk/directory/v1/guides/delegation#go)
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.api.services.admin.reports.Reports;
import com.google.api.services.admin.reports.ReportsScopes;
...
/** Email of the Service Account */
private static final String SERVICE_ACCOUNT_EMAIL = "<some-id>#developer.gserviceaccount.com";
/** Path to the Service Account's Private Key file */
private static final String SERVICE_ACCOUNT_PKCS12_FILE_PATH = "/path/to/<public_key_fingerprint>-privatekey.p12";
/**
* Build and returns a Reports service object authorized with the service accounts
* that act on behalf of the given user.
*
* #param userEmail The email of the user. Needs permissions to access the Admin APIs.
* #return Reports service object that is ready to make requests.
*/
public static Reports getReportsService(String userEmail) throws GeneralSecurityException,
IOException, URISyntaxException {
HttpTransport httpTransport = new NetHttpTransport();
JacksonFactory jsonFactory = new JacksonFactory();
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(ReportsScopes.ADMIN_REPORTS_AUDIT_READONLY)
.setServiceAccountUser(userEmail)
.setServiceAccountPrivateKeyFromP12File(
new java.io.File(SERVICE_ACCOUNT_PKCS12_FILE_PATH))
.build();
Reports service = new Reports.Builder(httpTransport, jsonFactory, null)
.setHttpRequestInitializer(credential).build();
return service;
}

Login Required 401 using Google ServiceAccountCredential using Google Admin Directory API

I have tried to follow the simple example listed here: https://developers.google.com/admin-sdk/directory/v1/quickstart/dotnet
The difference is I generated a Service Account Credential, and assigned it as a Delegate with the Role Project Owner, so it has full access. I also assigned it the proper namespaces for scopes.
Here it has access to orgunits which is what I'm trying to list in the Directory API
Here is my service account defined
Here are my credentials
I downloaded the JSON for the credential and added it to my project. I can confirm that the code loades the ServiceAccountCredential and successfully authenticates and gets an access token by inspecting the debugger.
But then I pass the credential to the Service Initializer, and when I create and execute a request it fails with
{"Google.Apis.Requests.RequestError\r\nLogin Required [401]\r\nErrors [\r\n\tMessage[Login Required] Location[Authorization - header] Reason[required] Domain[global]\r\n]\r\n"}
Here's the code:
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using System;
using System.Collections.Generic;
using System.IO;
namespace DirectoryQuickstart
{
class Program
{
static string[] Scopes = { DirectoryService.Scope.AdminDirectoryUser, DirectoryService.Scope.AdminDirectoryOrgunit };
static string ApplicationName = "slea-crm";
static string Secret = "gsuite-secret.json";
static void Main(string[] args)
{
ServiceAccountCredential sac = GoogleCredential.FromFile(Secret).CreateScoped(Scopes).UnderlyingCredential as ServiceAccountCredential;
var token = sac.GetAccessTokenForRequestAsync().Result;
// Create Directory API service.
var service = new DirectoryService(new BaseClientService.Initializer()
{
HttpClientInitializer = sac,
ApplicationName = ApplicationName,
});
OrgunitsResource.ListRequest request = service.Orgunits.List(customerId: "REDACTED");
IList<OrgUnit> orgUnits = request.Execute().OrganizationUnits;
if (orgUnits != null && orgUnits.Count > 0)
{
foreach (var orgUnit in orgUnits)
{
Console.WriteLine("{0} ({1})", orgUnit.Name, orgUnit.OrgUnitPath);
}
}
else
{
Console.WriteLine("No orgunits found.");
}
Console.Read();
}
}
}
Here is the content of my JSON secret (with redactions)
What am I missing here?
EDIT: OK, I breakpoint the code while it generates the request, and I can see that no where does it set the Authorization token bearer in the headers. Why? I would expect this HttpClientInitializer class to take care of that, since the API docs say it knows how to handle that, and every example on the internet I've found shows it just passing the credential into the service initializer. But when I walked through it, even though the credential has already been granted an access token and one exists within it, nowhere does the request have the header updated.
The only thing I can see is there is some way to add an HTTP request interceptor where possibly I could do this myself, but wow, this seems really...bizarre -- after all this work they did on the dotnet client SDK, I honestly could have just written direct to the HTTP API and it would have been a lot simpler and easier to follow.
The missing piece of the puzzle is this line:
ServiceAccountCredential sac = GoogleCredential.FromFile(Secret)
.CreateScoped(Scopes)
.UnderlyingCredential as ServiceAccountCredential;
Needs to be modified to this:
static string userName = "admin#yourdomain.com" // valid user in your org
ServiceAccountCredential sac = GoogleCredential.FromFile(Secret)
.CreateScoped(Scopes)
.CreateWithUser(userName)
.UnderlyingCredential as ServiceAccountCredential;
Java/Python/Go sample of doing similar is here: https://developers.google.com/admin-sdk/directory/v1/guides/delegation#create_the_service_account_and_its_credentials
This has been answered but adding more details here. If anyone wants to impersonate user to upload file on google drive using Service account. Follow these steps
Create Service Account
Enable Site Wide delegation for service account
Get Service account client ID
Enable Client ID to use Google Drive API using Google Admin Console->Manage API
Use the below C# code to upload file
public static DriveService GetService()
{
string[] scopes = new string[] { DriveService.Scope.Drive };
//"SERVICE_ACCOUNT_EMAIL_HERE";
String serviceAccountEmail = "test-417#elated-graph-261115.iam.gserviceaccount.com";
// Scope and user email id which you want to impersonate
var initializer = new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = scopes,
User = "yourEmail#domain.com"
};
//get private key, from .JSON file
var credential = new ServiceAccountCredential(initializer.FromPrivateKey("-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCkHeAicu6uFQn0\n7KUVTjgZ68nQui8+c8NmKW8aW8vhkBIKfdewXFECiUlTMPyI+HXbubsCK5Dl2xBS\nnphLq6YyE0xEQxNFLYHwfUKuzGQ2rV+qObcZ0mLZjCaf+pw3YiRVuU6OtslLJKJH\n-----END PRIVATE KEY-----\n"));
// Create the service.
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "DriveAPI",
});
service.HttpClient.Timeout = TimeSpan.FromMinutes(100);
return service;
}
That's it, we are done above Code is using Impersonation/Delegation for uploading file on Google Drive using Service account
Reference : Upload file to Google Drive using Service Account in C# MVC (With Impersonation)

Reusing Google API credentials in GData API

I am trying to make a web application is ASP.NET MVC 5 with which I can authenticate a user with a Google Account and then read data from his/her spreadsheets stored in Google Drive/Google Sheets.
I am using Google API to authenticate a user. After a user is successfully authenticated, I get the credentials back from Google in an object which is of type Google.Apis.Auth.OAuth2.Web AuthResult.UserCredential
I can then successfully create a service to list files from Drive using code similar to
var driveService = new DriveService(new BaseClientService.Initializer
{
HttpClientInitializer = result.Credential,
ApplicationName = "ASP.NET MVC Sample"
});
Now, I want to use GData API to read content from spreadsheets in Drive. For this to work, I need to have a SpreadsheetsService object and then set it's RequestFactory parameter to an instance of GOAuth2RequestFactory and this in turn needs OAuth2 parameters to be specified in an instance of class OAuth2Parameters.
How can I reuse the credentials obtained using the Google Api in GData API?
I am already doing the thing you want to do,
Code for how I passed the GData tokens
Issue with OAuth2 authentication with google spreadsheet
i.e. I use a single OAuth2 access/refresh token set. Using the same tokens for both gdata calls & drive API calls.
This is the code that finally worked for me
public class AppFlowMetadata : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "randomstring.apps.googleusercontent.com",
ClientSecret = "shhhhhh!"
},
Scopes = new[] { DriveService.Scope.Drive, "https://spreadsheets.google.com/feeds", "https://docs.google.com/feeds", "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile" },
DataStore = new FileDataStore("Drive.Api.Auth.Store")
});
public override string GetUserId(Controller controller)
{
var user = controller.Session["user"];
if (user == null)
{
user = Guid.NewGuid();
controller.Session["user"] = user;
}
return user.ToString();
}
public override IAuthorizationCodeFlow Flow { get { return flow; } }
}
And then, in the controller, OAuth2 parameters were copied to GData
var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).
AuthorizeAsync(cancellationToken);
OAuth2Parameters parameters = new OAuth2Parameters();
parameters.ClientId = "somestring.apps.googleusercontent.com";
parameters.ClientSecret = "shhhhhh!";
parameters.Scope = result.Credential.Token.Scope;
parameters.AccessToken = result.Credential.Token.AccessToken;
parameters.RefreshToken = result.Credential.Token.RefreshToken;
GOAuth2RequestFactory requestFactory = new GOAuth2RequestFactory(null, "MySpreadsheetIntegration-v1", parameters);
SpreadsheetsService service = new SpreadsheetsService("MySpreadsheetIntegration-v1");
service.RequestFactory = requestFactory;

Resources