I'm successfully using service accounts for domain-wide authority to access users' drive files and calendars as follows:
ServiceAccountCredential.Initializer initializer =
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = serviceAccountScope,
User = "user1#myDomain.com"
}.FromCertificate(certificate);
ServiceAccountCredential credential = new ServiceAccountCredential(initializer);
service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = appName,
});
However, I'm not entirely sure the best way to change users, especially if I'm dealing with many users at once. Is it best to just start over and create a new initializer and service for each user? Or is it possible to keep the same service and just update the imitated user?
The way ServiceAccountCredential was designed is that it's thread safe, so you can't change the User data. However, we may change the current getter property User to be similar Token (in the way that we look when getting and setting it). Please feel free to open a new issue in our issue tracker and elaborate more.
For now, you should use a new service and for each "user", as following:
ServiceAccountCredential.Initializer initializer =
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = serviceAccountScope,
User = "user1#myDomain.com"
}.FromCertificate(certificate);
var service1 = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = new ServiceAccountCredential(initializer),
ApplicationName = appName,
});
initializer.User = "user2#myDomain.com"
var service2 = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = new ServiceAccountCredential(initializer),`
ApplicationName = appName,
});
Related
I would like to get the email address of a user after successful sign-in. Google Plus APIs will be depreciated by Google. Any other way to access just email address of the user? Using the below code, I'll have access access token and id_token of the user.
UserCredential credential =
GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets { ClientId = clientId, ClientSecret = clientSecret }
, scopes
, userName
, CancellationToken.None
, new FileDataStore(fileDataStorePath)).Result;
Update: The following code worked.
var gmailService = new Google.Apis.Gmail.v1.GmailService(new BaseClientService.Initializer
{
HttpClientInitializer = credential,
ApplicationName = "App name"
});
var gmailProfile = gmailService.Users.GetProfile("me").Execute();
string EmailAddress = gmailProfile.EmailAddress;
Make sure that you have include the "email" scope as part of your scopes.
create a service object.
var service = new PeopleService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Peopleservice Oauth2 Authentication Sample"
});
Then make a request to the people api.
var results = service.People.Get("person/me").ExecuteAsync();
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 using this code and it works and allows my app to get the Calendar List from the used google account.
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = "xxx",
ClientSecret = "xxxx-xxxx",
},
new[] { CalendarService.Scope.Calendar },
"support#xxx.com",
CancellationToken.None).Result;
// Create the service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "xxx",
});
CalendarListResource.ListRequest cal = service.CalendarList.List();
cal.MaxResults = 10;
var calresult = cal.Execute().Items;
My question is when i do this i than import all the Calender's And Events to my local database so after an hour i need to run a script to update the Database with the latest Calendar and Event info as they might have added new Events or deleted some same goes with Calender's.
Not sure where to look for any suggestion?
For others:
The answer is here:
How to access users calendar list and events from google api using access token and refresh token
Basically you save the token and later user it for that Calendar.
I need some help - I'm still in the process of wrapping my head around the NEW Google Admin SDK using the Directory API. So far I'm able to create/delete a user, create/delete an Alias. I've authorized the scopes in those areas I need to etc.
My Question is how to I use the existing code below in order to use the GData Libraries? I need to use the "CreateSendAs()" function but that uses another set of includes that are different from the Directory API. Do I have to set an Auth2.0 token to something so that I can use the older GData calls?
I use for the Directory API:
const string serviceAccountEmail = "XXXXXX#developer.gserviceaccount.com";
const string serviceAccountCertPath = #"C:\XX-privatekey.p12";
const string serviceAccountCertPassword = "notasecret";
const string userEmail = "super-admin-user#domain.com";
var certificate = new X509Certificate2(serviceAccountCertPath, serviceAccountCertPassword, X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { DirectoryService.Scope.AdminDirectoryUser },
User = userEmail
}.FromCertificate(certificate));
var service = new DirectoryService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = APP_NAME,
});
This the C# code I need to use for the "CreateSendAs()" function.
using Google.GData.Apps;
using Google.GData.Apps.GoogleMailSettings;
using Google.GData.Client;
using Google.GData.Extensions;
GoogleMailSettingsService service = new GoogleMailSettingsService("yourdomain", "your-apps");
service.setUserCredentials("adminUsername", "adminPassword");
service.CreateSendAs("liz", "Sales", "sales#example.com", "", "true");
I am not sure how much correct the following approach is but that is what worked for me. Using the service credentials token you can access the GoogleMailSettingsService as follows
bool success = credential.RequestAccessTokenAsync(System.Threading.CancellationToken.None).Result;
OAuth2Parameters parameter = new OAuth2Parameters()
{
AccessToken = credential.Token.AccessToken
};
GOAuth2RequestFactory requestFactory = new GOAuth2RequestFactory("apps","your-apps", parameter);
GoogleMailSettingsService service = new GoogleMailSettingsService("yourdomain", "your-apps");
service.RequestFactory = requestFactory;
service.CreateSendAs("liz", "Sales", "sales#example.com", "", "true");
I am trying to get google users from my domain using google service account.
But it throws error
Error:"access_denied", Description:"Requested client not authorized.", Uri:""
My code
X509Certificate2 certificate = new X509Certificate2(key_path,
"notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer("publickey.gserviceaccount.com")
{ Scopes = scopes,
User = "admin#domain.com"
}.FromCertificate(certificate));
var service = new DirectoryService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "appname",
});
service.Users.List().Domain = "domain.com";
Users results = service.Users.List().Execute();
Thanks in advance
The service account email address needs to have access the domain. Take the email and add it as a user just enough access that it can read should be good.
Also did you change this for posting?
"publickey.gserviceaccount.com"
A service account email looks more like this:
539621478854-imkdv94bgujcom228h3ea33kmkoefhil#developer.gserviceaccount.com
You need to give your service-account/API project access to your domain first.
Steps detailed in the docs here:
https://developers.google.com/admin-sdk/directory/v1/guides/delegation#delegate_domain-wide_authority_to_your_service_account
You need to specify the correct scopes you need in step 6 of those instructions, which would be https://www.googleapis.com/auth/admin.directory.user.readonly to access the list of users.
In addition for the Directory API to work you need to enable API access in the domain settings: https://developers.google.com/admin-sdk/directory/v1/guides/prerequisites#set_up_api
I was finally able to get this working. Here is the code I have
var grpReq = service.Groups.List();
grpReq.Domain = "mydomain.com";
Groups groups = grpReq.Execute();
IList<Group> gps = groups.GroupsValue;
var memReq=service.Members.List(groups.GroupsValue[0].Id);
Members members = memReq.Execute();
I am still not sure why creating a var object and then Execute() got this to work but the earlier code didn't work.
I still have the problem of the consent screen showing up for all users. I have the following code. I think the way I get the logged in user's email is incorrect. Any ideas?
string mymail = googleauth.GetUsersEmail(ExchangeCodeWithAccessAndRefreshToken().Access_Token);
string path = "d:\\c6b82065f26fbb0-privatekey.p12";
X509Certificate2 certificate = new X509Certificate2(
path,
"notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer("876131792-v824u6drpss#developer.gserviceaccount.com")
{
User = mymail,
Scopes = new[] { PlusService.Scope.UserinfoEmail, PlusService.Scope.UserinfoProfile, PlusService.Scope.PlusMe }
}.FromCertificate(certificate));
PlusService plus = new PlusService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "myapp"
});
Person profile = plus.People.Get("me").Execute();
string email = profile.Emails[0].Value;