I'm running a process without UI that retrieves uploaded videos from youtube. On a develpement machine it authenticates on user behalf and it works.
The problem is when process is running on a server: browser window doesn't fire for user to let use his account (it should only require to do it once).
Event viewer shows no errors whatsoever. Service accounts doesn't seem to work with youtube, with API key is too less permissions and
oath is the only way to authenticate and get broadcasted videos. Or am I wrong?
So the question is: How to run a service as a single user and retrieve his videos without UI?
private async Task Run()
{
try
{
UserCredential credential;
using (var stream = new FileStream(StartPath + "\\client_secrets.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { YouTubeService.Scope.YoutubeReadonly, YouTubeService.Scope.Youtube },
"user",
CancellationToken.None,
new FileDataStore("Store")
);
}
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = this.GetType().ToString()
});
....
}
I have also tried:
String serviceAccountEmail = "e-mail";
var certificate = new X509Certificate2(HostingEnvironment.MapPath("~/Content/key.p12"), "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { YouTubeService.Scope.Youtube, YouTubeService.Scope.YoutubepartnerChannelAudit, YouTubeService.Scope.YoutubeUpload }
}.FromCertificate(certificate));
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "api",
});
The YouTube Data API lets you incorporate functions normally executed on the YouTube website into your own website or application. The lists below identify the different types of resources that you can retrieve using the API. The API also supports methods to insert, update, or delete many of these resources.
This reference guide explains how to use the API to perform all of these operations. The guide is organized by resource type. A resource represents a type of item that comprises part of the YouTube experience, such as a video, a playlist, or a subscription. For each resource type, the guide lists one or more data representations, and resources are represented as JSON objects. The guide also lists one or more supported methods (LIST, POST, DELETE, etc.) for each resource type and explains how to use those methods in your application.
The following requirements apply to YouTube Data API requests:
Every request must either specify an API key (with the key parameter) or provide an OAuth 2.0 token.
Your API key is available in the Developer Console's API Access pane for your project.
You must send an authorization token for every insert, update, and delete request. You must also send an authorization token for any request that retrieves the authenticated user's private data.
In addition, some API methods for retrieving resources may support parameters that require authorization or may contain additional metadata when requests are authorized. For example, a request to retrieve a user's uploaded videos may also contain private videos if the request is authorized by that specific user.
The API supports the OAuth 2.0 authentication protocol. You can provide an OAuth 2.0 token in either of the following ways:
Use the access_token query parameter like this: ?access_token=oauth2-token
Use the HTTP Authorization header like this: Authorization: Bearer oauth2-token
Complete instructions for implementing OAuth 2.0 authentication in your application can be found in the authentication guide.
So I've managed to make a work around that situation by creating console application program that does the thing.
Authentication mechanism moved into console application and passed the results to the service. In a service made a call into that application every n seconds. This solution worked like a charm. Ugly one, but it worked. Now i can give application access to profile data and use it where I want to.
If anyone will have a better solution I would be glad to read it.
Related
I have an app that allow me to read the data from Google Spreadsheet using API Key. I just make HTTP GET to this address and get a response with data.
https://sheets.googleapis.com/v4/spreadsheets/18soCZy9H4ZGuu**********BeHlNY1lD8at-Pbjmf8c/values/Sheet1!A1?key=AIzaSyAYJ***********pB-4iKZjYf4y0vhXP8OM
But when I try to do same to write data using HTTP PUT to address
https://sheets.googleapis.com/v4/spreadsheets/18soCZy9H4ZGuu**********BeHlNY1lD8at-Pbjmf8c/values/Sheet1!A4?valueInputOption=RAW?key=AIzaSyAYJ***********pB-4iKZjYf4y0vhXP8OM
its gives me 401 error.
Code to make PUT request:
using (WebClient wc = new WebClient())
{
byte[] res = wc.UploadData(link, "PUT", Encoding.ASCII.GetBytes(textBox1.Text));
MessageBox.Show(Encoding.Default.GetString(res));
}
Also spreadsheet is fully public with permission to read and write by anyone without auth. My guess is that I can't use API Key to write data to spreadsheet, and only way to do this is using OAuth.
UPDATE:
So i've just tryed Google.Apis.Sheets.v4 to write values, and now i'm almost 100% sure that API Key can't be used to write data to Google Spreadsheet. Well, then I'll use OAuth 2.0.
Well, maybe you are correct and the problem here is the API_KEY itself.
If you check the Sheets API documentation, it is stated that every request your application sends to the Google Sheets API needs to identify your application to Google. There are two ways to identify your application: using an OAuth 2.0 token (which also authorizes the request) and/or using the application's API key. Here's how to determine which of those options to use:
If the request requires authorization (such as a request for an individual's private data), then the application must provide an OAuth 2.0 token with the request. The application may also provide the API key, but it doesn't have to.
If the request doesn't require authorization (such as a request for public data), then the application must provide either the API key or an OAuth 2.0 token, or both—whatever option is most convenient for you.
So meaning either the OAuth 2.0 token or API key will work in your case since the file is public. But the problem is in the PUT request that you are doing, we can assume here that the API key is not working with it. But, we have alternatives for it, and that is the OAuth.
I also found here a related SO question that might help you.
For anyone still hoping for a simple answer, it seems there won't be one - any writing to a sheet, irrespective of the sheets permissions, will require OAuth2:
'This is intentional behavior. While public sheets are anonymously readable, anonymous edits aren't currently supported for a variety of reasons.
In this context, "anyone" == anyone with a google account.' HERE
One option that wasn't mentioned here is to use a service account instead. Service accounts are like users, but without being attached to a person. Instead, they're attached to a project.
Service accounts have an email address as well as a private key. Both can be used to create a JWTClientAuth, and this can be used to authenticate the API while it's being instantiated or to authenticate each and every request.
The advantage of the service account is that it works like an API KEY -- no need to ask a user to copy a URL to the browser and then copy a code back into the application -- but because it can act as an authenticated user, the service account email address can be added to the Google Sheet as an editor. With this in place, the application has full write access to the sheet but without having to deal with authorization codes and refresh tokens and copy/pasting.
You can see a Python example, Python With Google Sheets Service Account Step By Step, and a Node.js example, Accessing Google APIs Using Service Account in Node.js. I followed these examples to get setup.
Since you're using C#, you may find Writing to Google Sheets API Using .NET and a Service Account to be helpful.
This method reads the service account credentials from the JSON file to then instantiate the SheetsService:
private void ConnectToGoogle() {
GoogleCredential credential;
// Put your credentials json file in the root of the solution and make sure copy to output dir property is set to always copy
using (var stream = new FileStream(Path.Combine(HttpRuntime.BinDirectory, "credentials.json"),
FileMode.Open, FileAccess.Read)) {
credential = GoogleCredential.FromStream(stream).CreateScoped(_scopes);
}
// Create Google Sheets API service.
_sheetsService = new SheetsService(new BaseClientService.Initializer() {
HttpClientInitializer = credential, ApplicationName = _applicationName
});
}
Afterwards, you can use the Google Sheets .NET Client Library to write the data.
Ultimately, I am trying to upload a YouTube video on behalf of a user who has authorized my site / application to do so.
I have a website where a user authorizes the website to use the user's YouTube credentials. The website then properly stores the credentials after the user has authorized (YouTube Data API v3). The problem is, these credentials are desired to be used by a server process, which for all purposes would be considered a console application.
When adding credentials to my project on the Google API Manager, however, I can either do a Web Browser, Web Server, (and others) OR "other UI (Windows)". But I cannot do BOTH. I believe I need both because the user authorizes through the website, but the console process uses the credentials.
But even if I have an answer for that above, how do I pass the USER'S credentials to the console app using the sample code that they show in the samples?
using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
// This OAuth 2.0 access scope allows an application to upload files to the
// authenticated user's YouTube channel, but doesn't allow other types of access.
new[] { YouTubeService.Scope.YoutubeUpload },
"user",
CancellationToken.None
);
}
It seems like this code is used more for storing the credentials somewhere on the Windows file system to be retrieved later, like a cache. But I'm storing these "authorized" values in a database already, and would like to retrieve them and act on the behalf of that user.
I hope this makes sense, and I apologize if it rambled.
Background:
The Google .NET client library by default stores the credentials for the users in %AppData% the field where you have "user" is how its is storing it.
Example:
UserCredential credential;
using (var stream = new FileStream(clientSecretsJsonFilePath
,FileMode.Open
,FileAccess.Read))
{
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { DriveService.Scope.Drive, DriveService.Scope.DriveFile },
"LookIAmAUniqueUser",
CancellationToken.None,
new FileDataStore("Drive.Auth.Store")
).Result;
}
Assuming the user clicks accept on the authentication request screen, a new file will be created in that directory with the following structure:
Google.Apis.Auth.OAuth2.Responses.TokenResponse-LookIAmAUniqueUser.TokenResponse-LookIAmAUniqueUser
Each user will have their own file you change a user by changing the "LookIAmAUniqueUser" value.
Solution one:
Identify your users differently so that you know you are loading me vs you. Just by changing the "user" parameter, it will load the one needed or ask the user for authentication if it can't find it.
Solution two:
By default the library uses FileDataStore that's why I have it in mine and you don't have it in yours. If you are storing the credentials someplace else say the refresh token in the database along with your user information. You can create your own implementation of IDataStore which will load the credentials from there.
My article on FileDataStore might help you understand what its doing. Google .NET – FileDataStore demystified sorry I haven't had time to create a article on creating an implementation of IDataStore, however I might have an example or two laying around depends really on where you are storing those credentials
I am trying to authneticate gmail api using c# console application.
I am using.net sdk of Google api and the code i am using to authorize the api is as follows :
UserCredential credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = "clientId",
ClientSecret = "clientsecret"
},
new[] { GmailService.Scope.GmailModify },
"user",
CancellationToken.None, null);
But the above code open a browser window to ask for permission to access the service, I need to avoid this authentication activity as I need to schedule ".exe" file of the project on Azure
You're confusing between authorization and authentication.
Authorization is a one time process and you cannot authorize without user authorizing your app to do whatever you're meaning to do. It is a one time process.
What you're doing in the code is authorization. It will definitely open a browser for the user to authorize your app. Once you authorize your app, then for next time, all you need to do is authenticate. Authentication doesn't require this manual user process.
All you have to do is to use the UserCredential you receive from the Google from the next time you need it.
Store the credential you receive from the service somewhere and use it next time to initialize the Service
var service = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential, //your stored credential
ApplicationName = "App",
});
However you may need to refresh the Credential token (which expires every 60 minutes). It should automatically refresh, but you can refresh it manually everytime you need it by making a check.
if (credential.Token.IsExpired)
var returnBool = await credential.RefreshTokenAsync(cancellationToken);
I have been struggling with Google Calendar Api (V3) for quite a while, but I can now create events and list events for a given timespan. I am doing this in C# to create a desktop app.
I have two different google accounts, and I want to be able to post to each of those calendars separately. Using the code below (for the first google account) I am able to connect and post to the one calendar, but when I use the same code on a different form with the .json file from the 2nd google account, it comes back with error [404] not found when I attempt to list the events in that particular calendar. It is able to show a list of calendars BUT the ones listed are from the 1st google account. I have logged out of my google accounts in my web-browser, but my program consistently things I am working with the 1st google account.
PS: for the 2nd google account, I have a differntly named .json file, downloaded from the developer site while logged in with the 2nd google account. The app name for the 2nd google account is also named differently.
public async Task<int> testZZquery()
{
UserCredential credential;
using (var stream = new FileStream("CMgcal_client_secrets.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { CalendarService.Scope.Calendar },
"user", CancellationToken.None);
}
// Create the service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "mygcal",
});
It's the Oauth tokens that decide which account will be accessed, it's not the client secret or the project you are using. You can happily use the same project in the console for retrieving data from both the accounts. However, you will need to request a separate OAuth token for each of them and then use it for accessing the data.
The Oauth2 documentation is here: https://developers.google.com/accounts/docs/OAuth2
I am able to authenticate and perform actions on google drive using credential I got after authentication but this token is there for long long time. it says expires-in : 3600 so it should expire in 1hour but when I tried it after a month it uses that token and it worked.
My requirement is after authentication and whatever task is being performed get complete, it should again ask for authentication to user if user initiate the program again. so basically I don't want token to be stored in client's system. Expires-in is not working for me as token get refreshed and is not asking again for Authentication.
below is my code which I am using :
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = "<myid>.apps.googleusercontent.com",
ClientSecret = "<Mysecret>"
},
new[] { DriveService.Scope.Drive },
"user",
CancellationToken.None).Result;
// Create the service using the client credentials.
DriveService service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "sampleapp"
});
*****some task performed*********
now after this "some task" I want token to be destroy.
Please help.
From release 1.8.2 (http://google-api-dotnet-client.blogspot.com/2014/05/announcing-release-of-182.html) which was just released earlier today, we support token revocation. It revokes the token and also deletes it form the data store.
All you have to do is the following:
await credential.RevokeTokenAsync(CancellationToken.None);
As simple as that.
UPDATE:
Read more about token revocation in the Google APIs client library for .NET in this blogpost: http://peleyal.blogspot.com/2014/06/182-is-here.html
The reason that you user is not getting asked again is because you are passing a refresh token, you just didn't know it.
Normal when I do this I use the following code:
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = "<myid>.apps.googleusercontent.com",
ClientSecret = "<Mysecret>"
},
new[] { DriveService.Scope.Drive },
"user",
CancellationToken.None,
FileDataStore("Drive.Auth.Store")).Result;
What happens then is a file with my refresh token is stored in the user %AppData% directory with the name Drive.auth.store.
{
"access_token":"ya29.IgA23NyW3rF0xxoAAACFPgYzOm9Bm1Vixr-ti9crN5LvEr0W_gjtUQWOCy9QHw",
"token_type":"Bearer",
"expires_in":3600,
"refresh_token":"1/OGUwUcl92Abo-7LAsuC0tsp_FWxhOW_freUyKncNalI",
"Issued":"2014-05-26T20:34:07.447+02:00"
}
Now your code is slightly diffrent you arent sending FileDataStore("Drive.Auth.Store"). I hadn't tried not sending it before so I had to test this. It appears that by default GoogleWebAuthorizationBroker.AuthorizeAsync stores the refresh token for you just like it would if you used FileDataStore. The name appears to be a default Tasks.Auth.Store.
So what is happening is that every time your user runs your application the Client lib is auto loading that file for you.
How to fix it:
The ugly way would be to randomly change "User" (timestamp or something), if that is different then it will automatically prompt for authentication again. You will need to check the file in %appdata% i have no idea if its going to create a new one every time they run your application or if its just going to over wright the old one. Its probably not a good idea to keep creating crap files on the users PC.
The nice way. Create your own implementation of Idatastore that just doesn't save it. You can find a very basic example of that Google Oauth C# at the bottom called stored refresh token Just don't store it anyplace.