We develop application in C# which need to transfer ownership of all Google Drive documents related to the curtain domain to a single certain user without permission of original owner. We are using trial version of Google Apps business account.
In principal, we need to do this: http://screencast.com/t/effVWLxL0Mr4 but in C# code.
Accourding to the documentation, it is implemented in OAuth2 as superadmin functionality. https://support.google.com/a/answer/1247799?hl=en (1).
But document was deprecated and more over we did not find any API call to do that.
Using account of project creator, it is appeared, that he can not access to all files and can not see files is not shared with him.
In Google Admin Console in manage API client access we added access rights to him to access files without permission to files without permission. Link: screencast.com/t/zU9cc6Psyb. We added routes access routes there according to that document link: trovepromo-tf.trove-stg.com/0m1-sds/support.google.com/a/answer/162106?hl=en and tried again.
It did not work out...
Also, We found out that we need to use service account to have access to all data of all users of the domain, therefore we generated API keys for service account link: screencast.com/t/rNKuz6zchwV in the created project and got authenticated in the application using the following code:
var certificate = new X509Certificate2(#"C:\Temp\key.p12", "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer("Service account email")
{
User= "admin#domain.com",
Scopes = new[] { DriveService.Scope.Drive }
}.FromCertificate(certificate));
but when we try to get list of folders, we get error :"access_denied", Description:"Requested client not authorized.", Uri:""
Please help us to transfer ownership of one user to another by service account!
Update from 13-08-2014:
Dear, It seems I have problem with user impersonalization.
1) When I use api to connect on behalf of user. During the authentication it redirects to browser and ask permisstion. After that all is completely fine, I can manimulate with folders except one one thing: I can not transfer ownership to him
2) When I use service account without impersonalization, authentication looks like the following:
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(ServiceAccountId)
{
Scopes = new[] { DriveService.Scope.Drive,
DriveService.Scope.DriveAppdata,
DriveService.Scope.DriveFile,
DriveService.Scope.DriveScripts,
DirectoryService.Scope.AdminDirectoryUser,
DirectoryService.Scope.AdminDirectoryGroup,
DirectoryService.Scope.AdminDirectoryOrgunit,
DirectoryService.Scope.AdminDirectoryUserReadonly
},
}.FromCertificate(certificate));
Then I can access to all files shared to service account but (again) I can not transfer the rights.
3) Then I try impersonalize Service account by adding sypeadministrator email account to the user User = myaddress#mydomain.com
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(ServiceAccountId)
{
Scopes = new[] { DriveService.Scope.Drive,
DriveService.Scope.DriveAppdata,
DriveService.Scope.DriveFile,
DriveService.Scope.DriveScripts,
DirectoryService.Scope.AdminDirectoryUser,
DirectoryService.Scope.AdminDirectoryGroup,
DirectoryService.Scope.AdminDirectoryOrgunit,
DirectoryService.Scope.AdminDirectoryUserReadonly
},
User = AdminEmail,
}.FromCertificate(certificate));
Then I have Error:"access_denied", Description:"Requested client not authorized.", Uri:""
How can I impersonalize service account correctly?
Updated 13-08-2014
We found out that basic api for authentication is here: https://developers.google.com/accounts/docs/OAuth2ServiceAccount#creatingjwt
Normally, all what I showed before is an .net implementation of the protocol.
How can we please do impersonalization of of user in .net code. We did not find any working .net implementation of it.
I finally found an answer.
I could impersonalize an account by using the following construction:
Thanks to all, who tried to help me!
var initializer = new ServiceAccountCredential.Initializer(ServiceAccountId)
{
Scopes = scope,
User = AdminEmail1
};
var credential = new ServiceAccountCredential(initializer.FromCertificate(certificate));
var driveService = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName
});
Related
We have different projects on GCP we use them to access different Google APIs. Most of them for internal use only.
In this particular case, we have 2 projects, both use Service Account and both are allowed on Workspace Domain-wide Delegation on the same scopes. They are almost clones of each other.
I execute a simple request with the same code (Spreadsheet.Get()) with project 1 credentials it works. I execute the same request with project 2 credentials it doesn't work.
Since Workspace Domain-wide Delegation it's activated the spreadsheet its shared to my email and I connect to the API with my email too (works with project 1 so this is not the problem) (impersonating a user)
The only difference it's that one project has OAuth Consent Screen on external (only 100 users cause we use it internally only, anyways..) and the other one it's internal but this has nothing to do with this right?
Where the problem could come from? Do I need to recreate the project that doesn't work?
Here is the error message :
Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested
Edit to answer the comments but this code works depending on the service account we use
Generating the credentials:
internal static ServiceCredential GetApiCredentialsFromJson(string jsonCredentialsPath, string mailToMimic)
{
string jsonCertificate = File.ReadAllText(jsonCredentialsPath);
string privateKey = Regex.Match(jsonCertificate, #"(?<=""private_key"": "")(.*)(?="")").Value.Replace(#"\n", "");
string accountEmail = Regex.Match(jsonCertificate, #"(?<=""client_email"": "")(.*)(?="")").Value;
ServiceAccountCredential.Initializer credentials = new ServiceAccountCredential.Initializer(accountEmail)
{
Scopes = _scopes,
User = mailToMimic
}.FromPrivateKey(privateKey);
return new ServiceAccountCredential(credentials);
}
Using the credentials:
internal GoogleSheetService(ServiceCredential credentials)
{
SheetsService = new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credentials
});
SheetsService.HttpClient.Timeout = TimeSpan.FromSeconds(100);
}
Client ID is allowed on the Drive, Ads and Spreadsheets scopes on the Workspace console.
The answer was simple, but we had to figure it out by ourselves.
The scopes you add in your app when you initialize the client need to be exactly the same scopes you added in the Google Admin wide-delegation page. Even if your app or part of your app don't need them all.
C# example:
private static readonly string[] _scopes = { DriveService.Scope.Drive, SheetsService.Scope.Spreadsheets, SlidesService.Scope.Presentations };
ServiceAccountCredential.Initializer credentials = new ServiceAccountCredential.Initializer(accountEmail)
{
Scopes = _scopes,
User = mailToMimic
}.FromPrivateKey(privateKey);
return new ServiceAccountCredential(credentials);
Here my app only needs SheetsService.Scope.Spreadsheets but I had to add DriveService.Scope.Drive and SlidesService.Scope.Presentations because the same client its used for other apps that need them.
I want to create a Desktop Application which would control Google Home Devices. For the past couple of hours I've been struggling with the Google Homegraph authentication process.
I created a Service Account and added the JSON file to the project's file. I am creating the credentials this way:
string[] Scopes = { HomeGraphServiceService.Scope.Homegraph };
using (FileStream stream = new FileStream("ServiceCredentials.json", FileMode.Open, FileAccess.ReadWrite))
{
ServiceCred = ServiceAccountCredential.FromServiceAccountData(stream);
GoogleCredential credentials = GoogleCredential.FromServiceAccountCredential(ServiceCred).CreateScoped(Scopes);
var service = new HomeGraphServiceService(new BaseClientService.Initializer()
{
HttpClientInitializer = credentials,
ApplicationName = "Test Application"
});
}
To create any request, I need to provide a AgentUserId. Any ID from the ServiceCred or the service itself (ApiKey) results in an exception with a code 400 error. The credentials (of type GoogleCredential) don't have any sort of Id that I can provide.
How to make a request with a Service Account to get devices from the Google Home Graph?
Another thing is, when I try to add the HomeGraphServiceService.Scope.Homegraph scope anywhere in the Google Project - API Playground, this Application - it results in a code 400 error - Invalid Scope on the OAuth page.
I'm trying to configure an application able to work with Gmail API. As you know to work with it we must have an access token. There are several way of requesting this token, but for my needs it should be a service account, because in future this program code will be inside the Windows Service... (so, there is no opportunity to receive the token manually by redirecting from Google URL, only a web-request and response is a way out)
So, what I have done already:
Created new project (in Google Cloud Platform);
Created new service account in this project (according to the steps mentioned here: https://developers.google.com/identity/protocols/oauth2/service-account#creatinganaccount );
Generated and downloaded *.P12 key;
Enabled domain-wide delegation [before step 4 as were suggested in many similar questions];
Authorized the scope "https://mail.google.com/" in G Suite admin account for correct Client Id (according to the steps mentioned here: https://developers.google.com/identity/protocols/oauth2/service-account#delegatingauthority );
Used such simple code for authorization and requesting token:
const string serviceAccountEmail = "***test#oauthtester-271011.iam.gserviceaccount.com";
const string serviceAccountCertPath = #"C:\Users\user\Documents\Visual Studio 2017\Projects\OAuthTester\OAuthTester\bin\Debug\oauthtester-271011-bd2cced31ea5.p12";
const string serviceAccountCertPassword = "notasecret";
const string userEmail = "***oauthtest#***.com";
X509Certificate2 certificate = new X509Certificate2(
serviceAccountCertPath,
serviceAccountCertPassword,
X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { GoogleScope.ImapAndSmtp.Name }, //"https://mail.google.com/"
User = userEmail
}.FromCertificate(certificate));
credential.RequestAccessTokenAsync(CancellationToken.None).Wait();
Unfortunately, I'm facing with an error:
Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.
I have also tried:
To change serviceAccountEmail to ClientId;
To create, remove and add again the Authorized access in G Suite for the same Client Id;
To delete and create another service account and then Authorize new Client Id in G Suite.
Unfortunately, each time I'm facing with the same error. Maybe somebody guesses what I do wrong?
I'm trying to add events to my calendar.
I created service account credentials in Google Developers Console and I can authorize myself.
I have two calendars on my account, but I can't list them using c# code to google API.
Do I need some special permissions to my calendars?
On settings (calendar.google.com) tab I have full permissions.
Maybe there is something wrong with code which gets the calendar list?
string[] scopesCal = new string[] { CalendarService.Scope.Calendar, // Manage your calendars
CalendarService.Scope.CalendarReadonly, // View your Calendars
};
var certificate = new X509Certificate2( #"GigaNetLibGoogleXCalendar.p12", "notasecret", X509KeyStorageFlags.Exportable );
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer( ServiceAccountEmail ) {
Scopes = scopesCal
}.FromCertificate( certificate ) );
// Create the service.
CalSrv = new CalendarService( new BaseClientService.Initializer() {
HttpClientInitializer = credential,
ApplicationName = "XCalendar",
} );
var calendars = CalSrv.CalendarList.List().Execute().Items;
foreach ( CalendarListEntry entry in calendars )
Console.WriteLine( entry.Summary + " - " + entry.Id );
You are using a service account by default a service account doesn't have any Google calendars. You either need to add one or give it access to one of yours.
Take the service account email address and add it as a user on the calendar on your account you want it to be able to access.
Go to the Google Calendar website. Find the Calendar Settings , then go to the Calendars tab, find the calendar you want to access and click on “Shared: Edit settings” add the service account email address like you would a persons email address. This will give the service account the same access as if you where sharing it with any other user.
You will probably want to give it permission "make changes to events"
Tip
you only need the CalendarService.Scope.Calendar that will give it full access. You can remove the CalendarService.Scope.CalendarReadonly there is really no reason to have both.
Maybe I am simply not getting "it", with "it" being the overall setup needed to make this work.
I have a website that scrapes other sites for sporting events. I want to automatically create Google Calendar events from the results, so I want to give my Web Application Read/Write access on a Calendar in my GMail account.
I have been trying to wrap my head around this for a week now, but I can't get it to work and it is crushing my self-esteem as a developer.
The way I "understand" it is that I need a Google API v3 Service Account, because I don't need an API key for a particular user. Or do I need a Simple API key (instead of oAuth)?
Anyways, I went with the Service Account.
In my HomeController I am trying to GET a Calendar so I know it all works.
public void Calendar()
{
string serviceAccountEmail = "...#developer.gserviceaccount.com";
var certificate = new X509Certificate2(
Server.MapPath("~") + #"\App_Data\key.p12",
"notasecret",
X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential =
new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[]
{
CalendarService.Scope.Calendar
},
User = "MY-GMAIL-EMAIL" // Is this correct?
}
.FromCertificate(certificate));
BaseClientService.Initializer initializer = new BaseClientService.Initializer();
initializer.HttpClientInitializer = credential;
initializer.ApplicationName = "CALENDAR NAME"; // Correct?
var service = new CalendarService(initializer);
var list = service.CalendarList.List().Execute().Items; // Exception :-(
}
The error I am getting:
An exception of type 'Google.Apis.Auth.OAuth2.Responses.TokenResponseException' occurred in Google.Apis.dll but was not handled in user code
Additional information: Error:"unauthorized_client", Description:"Unauthorized client or scope in request.", Uri:""
So I tried a bunch of things in Google Calendar, like making it public, adding the service account email as a READ/WRITE user.
What do I need to do to authorize my Web Application so it can create events on my behalf?
I have done this with the service account in a similar post. I changed a bit of my code and got it working to list my calendars by switching a few things around. I can create events as well. I didn't add a user as you have done in the initializer, and under application name, it is the name of the application in the dev console. Make sure you name your application. Make sure your service account is shared with your account.
I slightly changed the list part of your code to this in mine and got back the my list of calendars.
var list = service.CalendarList.List();
var listex = list.Execute();
Check out my example at Google API Calender v3 Event Insert via Service Account using Asp.Net MVC