I trying to create a spreadsheet using the google spreadsheets v4. I generated a new server to server credential json on console api. All appears to work, but when I try to access the generated url, I got a screen saying to request permission. The credential was generated from my own service account, associated with my own google account, so I understand that I already have this permission. What could I do to access the generated sheet on browser? Above my code to generate the spreadsheet and the screen I getting.
public class Program
{
static string[] Scopes = { SheetsService.Scope.Spreadsheets, SheetsService.Scope.Drive, SheetsService.Scope.DriveFile };
static string ApplicationName = "Google Sheets API .NET Quickstart";
public static void Main(string[] args)
{
AppendData();
}
public static void AppendData()
{
// the downloaded jsonn file with private key
var credential = GoogleCredential.FromStream(new FileStream(Environment.CurrentDirectory + "/ApiKey/api_key.json", FileMode.Open)).CreateScoped(Scopes);
var service = new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
Spreadsheet requestBody = new Spreadsheet()
{
Properties = new SpreadsheetProperties()
{
Title = "Calculo Projeção",
},
};
SpreadsheetsResource.CreateRequest request = service.Spreadsheets.Create(requestBody);
}
}
So, the GSheet you've created was created using the service account. This means that the service account is the owner of the GSheet.
Depending on what your end goal is, and what type of Google Account you're using you may have some options here.
If you're using a G Suite account, the solution is relatively simple. It's to use impersonation with the service account. You're then basically using the service account to impersonate a user to perform the actions. If you impersonate your own account while creating the GSheet, the owner will be your own account. The documentation talks about the specifics here.
If you're NOT using a G Suite account, but a 'normal' Google account, it becomes a bit more tricky. I can think of some options here though:
Quick and dirty: Create the GSheet with your personal account first and give the service account access to it.
Have the service account create the GSheet and use the API/library to give your personal account access.
Create the GSheet not with the service account, but using OAuth and your own credentials. There's several ways to go about this. A good starting point for this would be here...
I may have missed one or two here, but you should understand what's actually happening now.
NOTE: One very important thing you have to take into account. If you delete the service account, any and all Google Drive items (including Sheets, Docs, Forms, etc) get deleted without any way of getting them back.
Related
I am getting the following error when trying to access the directory api by getting a list of users
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:""
Based on my previous googling efforts and reading stack overflow I am not sure what is causing this. As far as I am aware I have everything set up correctly. I have it setup to use the drive api in a very similar fashion and that works perfectly.
My service account does have domain wide delegation which is why I think it may have something to do with the second part of the error. Any ideas what could be causing this?
protected async virtual Task<DirectoryService?> GetDirectoryService()
{
if (currentDirectory == null)
{
string[] scopes = new string[] { DirectoryService.Scope.AdminDirectoryUser };
var initializer = new ServiceAccountCredential.Initializer(configuration["GoogleServiceAccount:AccountEmail"]){Scopes = scopes, User = configuration["GoogleServiceAccount:UserEmail"] };
var cred = new ServiceAccountCredential(initializer.FromPrivateKey(configuration["GoogleServiceAccount:SecretKey"]));
currentDirectory = new DirectoryService(new BaseClientService.Initializer { HttpClientInitializer = cred, ApplicationName = "DriveAPI" });
}
return currentDirectory;
User = configuration["GoogleServiceAccount:UserEmail"]
User is the user on your domain that you want to delegate as not the service accounts email address.
update
Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested
This error message in my exprence normally means that you are using code that does not match the type of credetinals you are using.
There are serval types of authorization, service account, Oauth installed and oauth web (lets ignore mobile for now). The code used for these credentials is different.
So if you use a service account key file with code designed for an Ouath2 installed app. You will normally get "Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested"
The code I normally use for delegation with a service account is as follows. it looks similar to yours so i'm inclined to agree that you are using service account code. Which means to me you are probably using the wrong key file. I would double check. Open it it should say "type": "service_account".
// Load the Service account credentials and define the scope of its access.
var credential = GoogleCredential.FromFile(PathToServiceAccountKeyFile)
.CreateWithUser("user#mydomain.com")
.CreateScoped(new[] {DriveService.ScopeConstants.Drive});
My suggestion is to now double check and ensure that you are using the service account key file from google cloud console that was created by a user on your domain, and that you configured domain wide deligation for and have added the admin sdk scopes for remember the OAuth Scopes for admin have to be set, as well configuring an authorized user.
I have a website, where I can create Events for members, and I am trying to connect to google calendar API, so when ever i create a new event, i can then sync that to the calendar.
I have followed this guide: https://developers.google.com/calendar/quickstart/dotnet
But when i get to the part where i run the sample code, and have to authenticate by OAuth.
I get the following:
My Code:
public class InitializeGoogleCalendarApiHelper : IInjected
{
public InitializeGoogleCalendarApiHelper(ILogger logger)
{
this.logger = logger;
}
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/calendar-dotnet-quickstart.json
static string[] Scopes = { CalendarService.Scope.CalendarReadonly };
static string ApplicationName = "Nordmanni Google Calendar API";
private readonly ILogger logger;
public CalendarService Initialize()
{
UserCredential credential;
using (var stream =
new FileStream("C:/Projects/Nordmanni/Nordmanni.App/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 = "C:/Projects/Nordmanni/Nordmanni.App/GoogleApiToken.json";
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
logger.Info<InitializeGoogleCalendarApiHelper>($"Credential file saved to: {credPath}");
}
// Create Google Calendar API service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
return service;
}
}
I am running the website locally for now, in visual studio.
The port number changes for each request i have made, so setting the port number does not seem to work.
These are the settings i setup in google.
I have spent the last day and a half, looking for a solution, but have been unable to find anything so far.
I have downloaded the credentials.json and added to the solution.
Any resources or links are welcome, or sample code which i could look at.
I am unsure if i have setup the domains correctly , or if it is even possible to setup while running it locally.
We had a similar problem a while ago, as far as I am assuming you are trying to authorise from your backend service for google API
Solution we employed
We used google service account for that. It is simple and elegant to use
Step - 1: Setup google service account
Follow this link to setup a service account using google developer console
https://developers.google.com/android/management/service-account
Step - 2: Share your google Application with service account email-id
This step gives your service account access to the google application may it be calendar or google docs or google sheets
Similar to sharing document with other users
https://support.google.com/calendar/answer/37082?hl=en (This link shows how to share your calendar with other emailId)
Step - 3: Follow these links to integrate service account with your application
https://developers.google.com/identity/protocols/oauth2/service-account
https://cloud.google.com/docs/authentication/production
Advantage of google service account is that you don't have to login at regular intervals to update the data. Set it up once and you can forget about it
Hope my answer helps :)
Since Google stopped support for their older Auth, and now we have to use oAuth 2, our simple desktop application can no longer read contacts from my google account.
Fine - i understand this, however this new oAuth 2 is extraordinarily complicated... and im not talking about from a developer perspective. From what i am reading online. We now have to make our customers jump over a multitude of hoops in order for our simple application to read contacts stored in their Google mail/Contacts.
My iPhone seems to be able to sync contacts just fine with just the typical email and password that i entered about a year ago. How do they get it to work? and yet with my simple desktop application, the client has to be rummaging around in Google Developer sites and with API settings etc. I'm a developer and im confused!! - could you imagine what my customer is going to go through... it cant be this complicated.
Is there anyone who can give me the simple 1,2,3 to get a C# desktop application to go off and get the contacts (read-only) from a particular Gmail account... with the least amount of fiddling around (for the owner of the Gmail account).
Ill do all the hard work in the application - i just don't want to client to have to spend an hour authorizing and creating API's and clicking around in a developer site (he/she is NOT a developer).
The main problem you have here is that contacts is an old Gdata API. It is possible to use Oauth2 with the Gdata library but its not pretty. Personally I like to hack things a little. I use the Current .net client library with the old Gdata client library.
Nuget New client library for authentication:
not 100% sure this is the only one you need let me know if it doesn't work we can find it. You basically need Google.apis.auth.oauth2 and google apis.util.store.
Install-Package Google.Apis.Auth
Nuget old client library for contacts:
Install-Package Google.GData.Contacts
Code
using Google.Apis.Auth.OAuth2;
using Google.Apis.Util.Store;
using Google.Contacts;
using Google.GData.Client;
using System;
using System.Threading;
public static void auth()
{
string clientId = "xxxxxx.apps.googleusercontent.com";
string clientSecret = "xxxxx";
string[] scopes = new string[] { "https://www.googleapis.com/auth/contacts.readonly" }; // view your basic profile info.
try
{
// Use the current Google .net client library to get the Oauth2 stuff.
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets { ClientId = clientId, ClientSecret = clientSecret }
, scopes
, "test"
, CancellationToken.None
, new FileDataStore("test")).Result;
// Translate the Oauth permissions to something the old client libray can read
OAuth2Parameters parameters = new OAuth2Parameters();
parameters.AccessToken = credential.Token.AccessToken;
parameters.RefreshToken = credential.Token.RefreshToken;
RunContactsSample(parameters);
Console.ReadLine();
}
catch (Exception ex)
{
Console.ReadLine();
}
Console.ReadLine();
}
/// <summary>
/// Send authorized queries to a Request-based library
/// </summary>
/// <param name="service"></param>
private static void RunContactsSample(OAuth2Parameters parameters)
{
try
{
RequestSettings settings = new RequestSettings("Google contacts tutorial", parameters);
ContactsRequest cr = new ContactsRequest(settings);
Feed<Contact> f = cr.GetContacts();
foreach (Contact c in f.Entries)
{
Console.WriteLine(c.Name.FullName);
}
}
catch (Exception a)
{
Console.WriteLine("A Google Apps error occurred.");
Console.WriteLine();
}
}
Tutorial can be found here
Google developers console
All applications accessing google apis must be registered on Google developers console. It is the application accessing Google that is registered users running the code do not need to do this step. Its you as a developer who has to register it.
From this you get the client id and client secret used in the code above.
I have done this but its all a bit of a blur from like you say, a lot of fiddling.
I think you can sign up and setup a project in google developer console and generate a service account. Then the client will need to sign in to HERE as the google app admin and fill out the clientID field with the name of your service account generated by the developer console and the API scope you need access to.
In the end I just logged in as the client to their admin panel and set it up for them. There is no easy way about it without the client also engaging a google apps re-seller to assist. I managed to figure it out as a developer with a lot of googling.
I have a problem. I use Azure AD to authenticate my asp.net app. Authentication works fine. Then I from this app trying to access OneDrive for Business using sharepoint search rest api. But the server always receives a response with a 401 error. I understand that the problem is in the access token which I use (Now I use the token received from Azure AD). But I never found the normal description of how to obtain an access token for the sharepoint search rest api.
Thanks in advance
Answer
You need to give your ASP.NET Application permission to use your OneDrive for Business application.
Here is an overview of how to do this using the Azure Management Portal. (Note that your OneDrive for Business account is a type of Office 365 SharePoint Online account.)
Go to manage.windowsazure.com > Active Directory > Your Tenant. If your tenant has an associated OneDrive for Business account, then its list of applications will include Office 365 SharePoint Online.
If your tenant's list of application does include Office 365 SharePoint Online, then your next step is to give your ASP.NET Web Application permission to access it.
Open up your Web Application's page in the Azure Active Directory area. Then choose CONFIGURE > Add Application. Add the Office 365 SharePoint Online application. Give it all necessary permissions and save.
The following screenshot is for a Native Client Application, because that is what my demo code is using. You can do a similar thing for a Web Application, though you will need to use an X509 Certificate for authentication instead of a username/password.
Your access token will now work with your Office 365 for Business account. Hooray!
Demo
Here is some sample code that works on my machine with a Native Client App. You can do the same thing with a Web Application, though you will need to use an X509 Certificate instead of a username/password.
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Net;
namespace AAD_SharePointOnlineApp
{
class Program
{
static void Main(string[] args)
{
var authContext =
new AuthenticationContext(Constants.AUTHORITY);
var userCredential =
new UserCredential(Constants.USER_NAME, Constants.USER_PASSWORD);
var result = authContext
.AcquireTokenAsync(Constants.RESOURCE, Constants.CLIENT_ID_NATIVE, userCredential)
.Result;
var token = result.AccessToken;
var url = "https://mvp0.sharepoint.com/_api/search/query?querytext=%27timesheets%27";
var request = WebRequest.Create(url);
request.Headers.Add(HttpRequestHeader.Authorization, "Bearer " + token);
var response = request.GetResponse() as HttpWebResponse;
}
}
class Constants
{
public const string AUTHORITY =
"https://login.microsoftonline.com/mvp0.onmicrosoft.com/";
public const string RESOURCE =
"https://mvp0.sharepoint.com";
public const string CLIENT_ID_NATIVE =
"xxxxx-xxxx-xxxxx-xxxx-xxxxx-xxxx";
public const string USER_NAME =
"MY_USER#mvp0.onmicrosoft.com";
public const string USER_PASSWORD =
"MY_PASSWORD";
}
}
Comments
If you are trying to do the above with a Web Application instead of a Native Client Application, then you will need to use an X509 Certificate, otherwise you will receive the following error.
Unsupported app only token.
See also: http://blogs.msdn.com/b/richard_dizeregas_blog/archive/2015/05/03/performing-app-only-operations-on-sharepoint-online-through-azure-ad.aspx
I need to make some changes to existing Spreadsheet in Google Drive. I want to do that periodically, using scheduled process. Due to this I decided to use service account authentication, which is described here https://developers.google.com/drive/web/delegation
The example is working fine and I can connect to the Plus API, however I need to connect to Google Spreadsheet API. The problem is that SpreadsheetsService does not seems to work with p12 files or Initializer class as DriveService does.
SpreadSheetsService has only 2 authentication methods which seems not to require to go by provided url - SetAuthenticationToken() and setUserCredentials(). There is no obvious way I can pass p12 file to the SpreadsheetService.
Did anyone solved this problem? I am okay with any "dirty" solution like decrypting p12 file (thought I don't think google provides password for this) or putting authentication headers from DriveService to SpreadsheetService. Did anyone solved this problem?
Or maybe there are 3rd parties libraries for C# that supports Spreadsheet API login via Service account?
Thanks.
Please refer to How to use SpreadsheetsService authenticated by ServiceAccountCredential?
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(ServiceAccount) {
Scopes = new[] {DriveService.Scope.Drive,"https://spreadsheets.google.com/feeds"}
}.FromCertificate(certificate)
);
bool success = credential.RequestAccessTokenAsync(System.Threading.CancellationToken.None).Result;
....
SpreadsheetsService spreadsheetsService = new SpreadsheetsService(applicationName);
var requestFactory = new Google.GData.Client.GDataRequestFactory(applicationName);
requestFactory.CustomHeaders.Add(string.Format("Authorization: Bearer {0}", credential.Token.AccessToken));
spreadsheetsService.RequestFactory = requestFactory;