I have a question about how correctly add a contact to Google Contacts using Google API.
For authorization I use external Json file Generated.
When I execute it , it doesn't give any mistakes but No Contact is Added to Google Contacts.
What can be wrong with the code?
Please find code below
Thanks
private async Task Run()
{
GoogleCredential credential;
using (Stream stream = new FileStream(#"D:\project1.json", FileMode.Open, FileAccess.Read, FileShare.Read))
{
credential = GoogleCredential.FromStream(stream);
}
string[] scopes = new string[] {
PeopleServiceService.Scope.Contacts,
PeopleServiceService.Scope.ContactsReadonly,
PeopleServiceService.Scope.ContactsOtherReadonly,
};
credential = credential.CreateScoped(scopes);
BaseClientService.Initializer initializer = new BaseClientService.Initializer()
{
HttpClientInitializer = (IConfigurableHttpClientInitializer)credential,
ApplicationName = "Project1",
GZipEnabled = true,
};
PeopleServiceService service = new PeopleServiceService(initializer);
Person contactToCreate = new Person();
List<Name> names = new List<Name>();
names.Add(new Name() { GivenName = "Alex", FamilyName = "Breen", DisplayName = "Alex Breen" });
contactToCreate.Names = names;
List<PhoneNumber> phoneNumbers = new List<PhoneNumber>();
phoneNumbers.Add(new PhoneNumber() { Value = "11-22-33" });
contactToCreate.PhoneNumbers = phoneNumbers;
List<EmailAddress> emailAddresses = new List<EmailAddress>();
emailAddresses.Add(new EmailAddress() { Value = "AlexBreen#mail.com" });
contactToCreate.EmailAddresses = emailAddresses;
PeopleResource.CreateContactRequest request = new PeopleResource.CreateContactRequest(service, contactToCreate);
Person createdContact = request.Execute();
Console.WriteLine(request);
}
Results
Metrics
You need to go though your service object.
var request = service.People.CreateContact(new Person()
{
Names = new List<Name>() { new Name() { DisplayName = "test"}}
// Fill in the rest of the person object here.
});
var response = request.Execute
Make sure you are checking google contacts from the same user you are authenticating your application from.
The response should be returning the new user.
all contacts for a user
You can also test it by doing. This will give you a list of the users inserted for the user you have authorized.
var results = service.People.Connections.List("people/me").Execute();
who is the current user
var results = service.People.Get("people/me").Execute();
var results = service.People.Connections.List("people/me").Execute();
service accounts
A service account is not you. Think of a service account more as a dummy user it has its own Google contacts account. When you insert into it you are inserting into the account owned by the service account.
If you have google workspace you can set up domain wide deligation to the service account and then delegate to users on the domain and add contacts to their google contacts within the domain.
You can not use a service account to write to a standard google gmail user's google contacts. For that you would need to use Oauth2 and authorize the user to access their google contacts.
Based on the comments on the previous answer:
You are inserting contacts to a service account
To use your own account you have to create an OAuth client ID and then use the credentials.json to authorise on the code.
There is no C# sample on the People API samples but you can check on how to use this credentials based on the .NET quickstart for Drive API but without using Drive API scopes and code.
Basically using this part of the code:
UserCredential credential;
using(var stream =
new FileStream("credentials.json", FileMode.Open, FileAccess.Read))
{
// The file token.json stores the user's access and refresh tokens, and is created
// automatically when the authorization flow completes for the first time.
string credPath = "token.json";
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
Related
I'm using Google Sheet API V4 in C#.
My source will create new Excel file, write data to that file.
When execute, Google API will open a new OAuth tab on the browser, User will choose/login to an email on this tab, and grant Read/Write/All permission for my App.
My question is: How can I get Logged/permitted email address in C# source?
I want to know, what email did the user log in to create myfile.xlsx.
Thanks in advance.
Code snippet:
string[] Scopes = { SheetsService.Scope.DriveFile };
string ApplicationName = "Google Sheets API";
UserCredential credential;
using (var stream =
new FileStream("credentials.json", FileMode.Open, FileAccess.Read))
{
// The file token.json stores the user's access and refresh tokens, and is created
// automatically when the authorization flow completes for the first time.
string credPath = "token.json";
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"UserName",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
var fileName = "myfile.xlsx";
var myNewSheet = new Spreadsheet();
myNewSheet.Properties = new SpreadsheetProperties();
myNewSheet.Properties.Title = fileName;
var sheet = new Sheet();
sheet.Properties = new SheetProperties();
sheet.Properties.Title = $"data";
myNewSheet.Sheets = new List<Sheet>() { sheet };
var newSheet = service.Spreadsheets.Create(myNewSheet).Execute();
var spreadSheetId = newSheet.SpreadsheetId;
...........
// Write data to file source
Google Indentiy platform describes the procedure of how to authenticate a user to obtain user information and more specifically user profile information
The documentation does not specify how exactly to do it in C#, but basically
Make a GET request to https://www.googleapis.com/oauth2/v3/userinfo
Provide the scopes https://www.googleapis.com/auth/userinfo.profile and https://www.googleapis.com/auth/userinfo.email
There is a good sample on Stackoverflow how to do it in PHP - I hope it helps you with the implementation in C#
There is also a workaround authenticating users with Gmail
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:
Setup Service Account
Enabled GSuite domain-wide delegation on service Account
Enabled API Access
Added the Service Accounts client ID to API Client Access with the scope (https://www.googleapis.com/auth/admin.directory.user)
Here's the code that i'm using.
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(_googleServiceSettings.Client_Email)
{
ProjectId = _googleServiceSettings.Project_Id,
User = "superadmin#google.com",
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("user#google.com");
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:""
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 user1#example.com is the email address for the G Suite user for which information will be displayed.
The user admin#example.com 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 = "admin#example.com";
// G Suite User to get information about
var gs_email = "user1#example.com";
// 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.");
}
}
}
}
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;
}
}
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
I am creating file on Google drive with .NET client API with Service account.
string[] scopes = new string[] { DriveService.Scope.Drive };
GoogleCredential credential;
using (var stream = new FileStream(Directory.GetCurrentDirectory() + "/Resources/GoogleCredentials.json", FileMode.Open, FileAccess.Read))
{
credential = GoogleCredential.FromStream(stream).CreateScoped(scopes);
}
DriveService drive = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
});
I succesfully create file,
var f = drive.Files;
var request = f.Create(new Google.Apis.Drive.v3.Data.File()
{
Name = "Test from ASP.NET Core",
AppProperties = prop,
MimeType = "application/vnd.google-apps.document"
});
var file = await request.ExecuteAsync();
share it with all domain, but I can not transfer ownership to a domain user.
Permission permission = new Permission()
{
EmailAddress = "user#example.com",
Type = "user",
Domain = "example.com",
Role = "owner"
};
var requestpermission = drive.Permissions.Create(permission, file.Id);
requestpermission.TransferOwnership = true;
var perm = await requestpermission.ExecuteAsync();
I get error:
The specified domain is invalid or not applicable for the given
permission type.
I found this link, but using p12 cert file is not recommended. So I want to use JSON.
Ownership transfers can only be done between users in the same domain, and service accounts don't belong to any domain. You're best option may be to create a Team Drive that the service account has access to, and perform a two stage process:
Use the service account to move the file into the team drive. Files.update with the addParents parameter set to the Team Drive ID.
Use the domain user to move the file out of the team drive. Files.update with the addParents parameter set to root (or some target folder's ID) and the removeParents parameter set to the Team Drive ID.
I have created an Alexa skill and enabled account linking. Setup my skills configuration to point to googles authorization and token servers, the skill successfully links. Then alexa sends POST Requests to my service that include the access token and state from Google. I use this to validate the token and what I want to do is use the token to access gmail api and begin calling actions.
However, the only examples online I can find include re-building the gmailservice by using the client secret and client id again. I figure since I have the access token already, why do I need to include the secret and ID again? How can i make gmail api calls with the access token I already have, or is this not possible? Here's the code I'm trying to use:
UserCredential credential;
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/gmail-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 Gmail API service.
var service = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
// Define parameters of request.
UsersResource.LabelsResource.ListRequest request = service.Users.Labels.List("me");
// List labels.
IList<Label> labels= request.Execute().Labels;
Console.WriteLine("Labels:");
if (labels != null && labels.Count > 0)
{
foreach (var labelItem in labels)
{
Console.WriteLine("{0}", labelItem.Name);
}
}
else
{
Console.WriteLine("No labels found.");
}
Console.Read();
I don't want to include the client_secret.json file because I already was authorized by google's oauth process in the alexa skill's account linking. Help ?
I have a desktop application to read mail using GMAIL API over REST Interface. I want to use service account so that we can download the mails using domain setting and user interaction is null. I am successfully able to create Gmail Service instance but when I try to access any Gmail API method like fetching mail list or any other I get an exception saying
Google.Apis.Auth.OAuth2.Responses.TokenResponseException:
Error:"access_denied", Description:"Requested client not
authorized."
I am done with all the setting at developer console and added scopes to my gapps domain.
Does Gmail API support service account? Using the same setting and service account I am able to get list of all files in Google drive using Drive service and API.
I use the following C# code for accessing Gmail from Service Account
String serviceAccountEmail =
"999999999-9nqenknknknpmdvif7onn2kvusnqct2c#developer.gserviceaccount.com";
var certificate = new X509Certificate2(
AppDomain.CurrentDomain.BaseDirectory +
"certs//fe433c710f4980a8cc3dda83e54cf7c3bb242a46-privatekey.p12",
"notasecret",
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
string userEmail = "user#domainhere.com.au";
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
User = userEmail,
Scopes = new[] { "https://mail.google.com/" }
}.FromCertificate(certificate)
);
if (credential.RequestAccessTokenAsync(CancellationToken.None).Result)
{
GmailService gs = new GmailService(
new Google.Apis.Services.BaseClientService.Initializer()
{
ApplicationName = "iLink",
HttpClientInitializer = credential
}
);
UsersResource.MessagesResource.GetRequest gr =
gs.Users.Messages.Get(userEmail, msgId);
gr.Format = UsersResource.MessagesResource.GetRequest.FormatEnum.Raw;
Message m = gr.Execute();
if (gr.Format == UsersResource.MessagesResource.GetRequest.FormatEnum.Raw)
{
byte[] decodedByte = FromBase64ForUrlString(m.Raw);
string base64Encoded = Convert.ToString(decodedByte);
MailMessage msg = new MailMessage();
msg.LoadMessage(decodedByte);
}
}
Here is a little bit of python 3.7:
from google.oauth2 import service_account
from googleapiclient.discovery import build
def setup_credentials():
key_path = 'gmailsignatureproject-zzz.json'
API_scopes =['https://www.googleapis.com/auth/gmail.settings.basic',
'https://www.googleapis.com/auth/gmail.settings.sharing']
credentials = service_account.Credentials.from_service_account_file(key_path,scopes=API_scopes)
return credentials
def test_setup_credentials():
credentials = setup_credentials()
assert credentials
def test_fetch_user_info():
credentials = setup_credentials()
credentials_delegated = credentials.with_subject("tim#vci.com.au")
gmail_service = build("gmail","v1",credentials=credentials_delegated)
addresses = gmail_service.users().settings().sendAs().list(userId='me').execute()
assert gmail_service
If you want to "read mail" you'll need the newer Gmail API (not the older admin settings API that 'lost in binary' pointed out). Yes you can do this with oauth2 and the newer Gmail API, you need to whitelist the developer in Cpanel and create a key you can sign your requests with--it take a little bit to setup:
https://developers.google.com/accounts/docs/OAuth2ServiceAccount#formingclaimset
For C# Gmail API v1, you can use the following code to get the gmail service. Use gmail service to read emails. Once you create the service account in Google Console site, download the key file in json format. Assuming the file name is
"service.json".
public static GoogleCredential GetCredenetial(string serviceAccountCredentialJsonFilePath)
{
GoogleCredential credential;
using (var stream = new FileStream(serviceAccountCredentialJsonFilePath, FileMode.Open, FileAccess.Read))
{
credential = GoogleCredential.FromStream(stream)
.CreateScoped(new[] {GmailService.Scope.GmailReadonly})
.CreateWithUser(**impersonateEmail#email.com**);
}
return credential;
}
public static GmailService GetGmailService(GoogleCredential credential)
{
return new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Automation App",
});
}
// how to use
public static void main()
{
var credential = GetCredenetial("service.json");
var gmailService = GetGmailService(credential);
// you can use gmail service to retrieve emails.
var mMailListRequest = gmailService.Users.Messages.List("me");
mMailListRequest.LabelIds = "INBOX";
var mailListResponse = mMailListRequest.Execute();
}
Yes you can... check the delegation settings...
https://developers.google.com/admin-sdk/directory/v1/guides/delegation#delegate_domain-wide_authority_to_your_service_account
Edit: Use the link Eric DeFriez shared.
You can access any user#YOUR_DOMAIN.COM mails/labels/threads etc. with the new Gmail API:
https://developers.google.com/gmail/api/
via service account with impersonation (service account is accessing api as if it was specific user from your domain).
See details here: https://developers.google.com/identity/protocols/OAuth2ServiceAccount
Here is relevant code in Dartlang:
import 'package:googleapis_auth/auth_io.dart' as auth;
import 'package:googleapis/gmail/v1.dart' as gmail;
import 'package:http/http.dart' as http;
///credentials created with service_account here https://console.developers.google.com/apis/credentials/?project=YOUR_PROJECT_ID
final String creds = r'''
{
"private_key_id": "FILL_private_key_id",
"private_key": "FILL_private_key",
"client_email": "FILL_service_account_email",
"client_id": "FILL_client_id",
"type": "service_account"
}''';
Future<http.Client> createImpersonatedClient(String impersonatedUserEmail, List scopes) async {
var impersonatedCredentials = new auth.ServiceAccountCredentials.fromJson(creds,impersonatedUser: impersonatedUserEmail);
return auth.clientViaServiceAccount(impersonatedCredentials , scopes);
}
getUserEmails(String userEmail) async { //userEmail from YOUR_DOMAIN.COM
var client = await createImpersonatedClient(userEmail, [gmail.GmailApi.MailGoogleComScope]);
var gmailApi = new gmail.GmailApi(client);
return gmailApi.users.messages.list(userEmail, maxResults: 5);
}