Allowing Google Service Account YouTube Upload Access via API v3 - youtube-api

I want to automatically upload videos to YouTube without user involvement so I've created a service account, jumped through the hoops, all was looking great, then the upload, chunk one, is attempted and my code bombs with this Google_Exception exception:
"Failed to start the resumable upload (HTTP 401: youtube.header, Unauthorized)"
I then dug and found on the YouTube API v3 error information:
https://developers.google.com/youtube/v3/docs/errors
"This error is commonly seen if you try to use the OAuth 2.0 Service Account flow. YouTube does not support Service Accounts, and if you attempt to authenticate using a Service Account, you will get this error."
Is this correct? I cannot use a service account to upload video to YouTube automatically?
(that was a waste of a couple of days hard work!)

Yes, it is correct.
The way forward is to do a manual authorisation and grab the resultant 'refresh token' and then use that for any automated uploads.
Ensure to add the refresh token to the PHP Google_Client object prior to any other action.
I am now automatically uploading to YouTube.

For anyone attempting to do this today, be aware that any uploads will be set as "Private (Locked)" if they are uploaded using the API unless/until the app is submitted for verification and approved.
https://github.com/porjo/youtubeuploader/issues/86
YouTube requires verification of the OAuth App/credentials used for upload, otherwise the video will be locked as Private.
It's possible to get an app approved, but if you're doing a personal project, it's less likely.
More: https://github.com/porjo/youtubeuploader/issues/86

Yes you can use Service Accounts for Youtube. One way is to add the service account to a CMS where the content owner is part of. When you retrieve an access_token for the Service Account, you can work with the content owners videos.
Looks like this, using Javascript and a npm package:
import { google } from 'googleapis';
const youtube = google.youtube('v3');
const fetchYoutubeData = async (keyfile, scopes) => {
const auth = new google.auth.GoogleAuth({
credentials: keyfile,
scopes,
});
const accessToken = await auth.getAccessToken();
const { data } = await youtube.videos.list({
part: ['snippet'],
id: 'YOUR_VIDEO_ID',
access_token: accessToken
});
return data;
}
Note that the keyfile is the JSON you downloaded from Gcloud when creating the service accounts keys. NOT the path pointing to the file! If you want to use path, use this instead:
const auth = new google.auth.GoogleAuth({
keyFile: 'PATH_TO_KEYFILE_IN_FILE_SYSTEM',
scopes: ['READ_STUFF', 'WRITE_STUFF'],
});
Further reading on server-server authentication using Oauth2

Related

Youtube Data API - How to avoid Google OAuth redirect URL authorization

Requirement: I am trying to upload videos to my Youtube channel through Youtube Data API for Java. The request is sent from a war file hosted on tomcat container.My application is not for external users and I only use it to upload my own generated videos. With the help of the api documentation and sample youtube code snippets, I have successfully managed to post video on youtube.
Problem: The issue is that whenever I try to run the code, I get prompted for
Please open the following address in your browser:
https://accounts.google.com/o/oauth2/auth?client_id=&redirect_uri=http://localhost:8080/Callback&response_type=code&scope=https://www.googleapis.com/auth/youtube.upload
Since I run this code on a remote server, it is not possible for me to always open this URL on the browser. Since I have registered my web app in Google Console, and got a pair of Client ID and Secret and a JSON file, so Youtube must allow me to publish videos by default to atleast my channel, isin't it?
I have used the Auth.java file(provided in youtube java code samples) and the following code is where this thing happens.
// Authorize.
return new AuthorizationCodeInstalledApp(flow, localReceiver).authorize("user#.com");
LocalServerReceiver localReceiver = new LocalServerReceiver.Builder().setPort(8080).build();
Please help here as this is really eating up a lot of my development time.
You should only need to authenticate your code once. When your code is authenticated you get a refresh token back. the refresh token will then allow you to get a new access token the next time your code runs.
There is no service account authentication for the YouTube api. Your code has to be authenticated by you the first time in order to get that refresh token.
I am not a java programmer but from a quick check of the documentation it looks quite similar to what I do in.net. You need to create a datastore to to store the first refreshh token then you should be able to run your code in the future with out needing to authenticate it again.

How to Upload To YouTube v3 using Access Token?

I've gone through OAuth on a web application and obtained the access token...
Now, I figure I should use that access token to upload a video, but the API v3 doesn't seem to let me use it. I'm looking at YouTube Data API: .NET code samples. In particular, this line seems strange to me:
UserCredential credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(secrets,
new[] { "YouTubeService.Scope.YoutubeUpload" },
"user",
CancellationToken.None);
The third parameter user, I guess is the user name. I don't get that info from an OAuth2 response. I don't even know what is a YouTube user name - I only see display names when I look at people's YT profiles, and those display names are NOT unique. What is this third parameter?
The function name AuthorizeAsync implies that we have yet to obtain authorization - but then why go through OAuth in the first place? Having an access token to me means that the user already authorized our app to upload.
I also found this possibility of being able to pass the access token:
var token = new TokenResponse()
{
AccessToken = "xm239jjks9f98900....."
};
UserCredential credential = new UserCredential(new GoogleAuthorizationCodeFlow(
new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = secrets
}
), "userId", token);
//GoogleAuthorizationCodeFlow
YouTubeService youTubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = Assembly.GetExecutingAssembly().GetName().Name
});
Just by the name of "TokenResponse" seems like I shouldn't build it explicitly. Also, GoogleAuthorizationCodeFlow.Initializer has the parameter for userId, which gain, I don't know because that's not given to me by OAuth2.
The BaseClientService.Initializer() also has the ApplicationName, which is what? I don't think it's Assembly.GetExecutingAssembly().GetName().Name as I copied from somewhere.
I tried to run the code above and I'm getting the error "The access token has expired but we can't refresh it" - but the user has just logged in within the last few seconds, so that access token is new.
Sorry if I'm asking something obvious, but the official docs don't tell me anything and similar questions to what I'm asking here on SO remain unanswered. Please help!
UPDATE:
In my case, all of the code resides inside a custom-made Web API function, but I think it will work the same on any server-side page (.aspx.cs) or MVC controller.
I found out that we DO keep the literal string "user" no matter who might be logged into his/her Google account. I guess the internal workings of UserCredential or GoogleWebAuthorizationBroker know how to pull the right data, maybe from cookies (?), and the UploadAsync function will know to which account to upload the video to.
I also used the GoogleWebAuthorizationBroker instead of building the UserCredential and Token from scratch (which resulted in the "expired token" message).
Now, the thing that I luckily stumbled upon after desperately trying anything, is that I needed to set up an Installed Application (Native) of type Other, which is SEPARATE from the web application. That Installed Application will have its own set of client id and client secret. Using those credentials, the upload worked!
... But there are some things that are quite not right. The user will be asked to authenticate your client app again - but there will be a new tab open for that consent, and when you click Accept (or OK), you'll land an empty page that says something like "access code received". Your upload actually happened but this is a very dirty and unacceptable user experience issue.
I'm still not satisfied as what I think I did is hacky/wrong.
I ran into this exact problem. By examining the code, I discovered that you can work around the problem by setting the Issued and ExpiresInSeconds properties on TokenResponse:
token.Issued = DateTime.Now;
token.ExpiresInSeconds = int.MaxValue;
It's a hack, but it works like a charm! It seems like the authors of the .net client assumed that you'll always have a refresh token, as they make it difficult to use the access token alone. The java and python libraries are much more intuitive in this regard.
It does not seem to matter what you pass for the user strings.

Youtube : Multiple accounts authentication (OAuth) to upload video to single account/channel

I am integrating video upload to Youtube from iOS application.
Steps followed :
Created Google account and enable Youtube data API for the account in developer console.
Created client ID and client secret and incorporated in the application.
Checked out Google API objective C client for Youtube upload which is normally a MAC tool, i just modified for iOS.
Run the application.
It will prompt for Login (OAuth).
Case # 1
Use the same account for Login, which used for creating client ID and
Client secret.
Accept the scopes and assign the authorizer to Youtube service.
GTMOAuth2ViewControllerTouch :
completionHandler:^(GTMOAuth2ViewControllerTouch *viewController,
GTMOAuth2Authentication *auth, NSError *error)
{
self.youTubeService.authorizer = auth;
}
start upload and it working fine.
Case #2
Use Different account for login.
Accept the scopes and assign the authorizer to Youtube service.
GTMOAuth2ViewControllerTouch :
completionHandler:^(GTMOAuth2ViewControllerTouch *viewController,
GTMOAuth2Authentication *auth, NSError *error)
{
self.youTubeService.authorizer = auth;
}
start upload and it causing : Error Domain=com.google.GTLJSONRPCErrorDomain Code=-32602 "The operation couldn’t be completed. (Unauthorized)" UserInfo=0x986abe0 {error=Unauthorized, GTLStructuredError=GTLErrorObject 0x98528d0: {message:"Unauthorized" code:-32602 data:1}, NSLocalizedFailureReason=(Unauthorized)}
Note : If i uses same account (used to create client ID and secret) to login and upload, it is working fine and video is displaying in Youtube.
But whenever trying with other account to upload it causing problem even in Google API objective C client application too.
let me know if i missing any other cases to be consider to do so.
thanks.
You may be doing Auth wrong, or the user may not have a valid connect YouTube channel.
You can check YouTube Direct Lite for iOS project for the best example.
After a day of search/testing, finally found the cause.
There is nothing wrong with my OAuth process.
The cause is other multiple accounts (not the one used to create Client ID and Client Secret) was not having a channel in Youtube.
Solution :
If user account is new to Youtube, then youtube won't have a channel for that account.
User need to create one (that's easy, Youtube itself will alert to create new channel when, My Channel from left side pane is get selected)
If user account have valid channel, then the above "Unauthorized" error won't be happen.

YouTube API broken by Google. 'Authentication with Google failed. Reason: NoLinkedYouTubeAccount'

** UPDATE **
It truly seems that Google has just screwed every single person on the planet by absolutely requiring user interaction to upload a video. Of course I know, they are free. Exactly what I warned the client years ago about, so I don't need to be reminded. Thank You.
So I would like to try to take this in a different direction and just find a loophole and a workaround to still keep doing what we are doing in spite of Google's complete lack of support or caring in any way about the developers and what they have to deal with.
It would be different if you can actually call a phone number and talk to a human being about YouTube Partner access, but you can more quickly get access to the Illuminati.
OAuth 2.0 is now the only supported authentication method period. It does require user interaction.
But what about that token? Does anybody know how long the token lasts?
If I can obtain a token just once using user interaction and place it in the database, I can automate possibly hundreds or thousands of interactions afterwards.
In other words, I'm trying to turn the user interaction into a speed bump instead of a concrete wall.
If anybody has any examples of obtaining that token, caching it, and using it afterwards, that would be a godsend to me right now.
Thanks for the comments and the help. I'm not surprised that the YouTube Developers Forum just folded and said to come here instead :)
It seems that Google has completely pulled the plug on the existing dashboard.
https://code.google.com/apis/youtube/dashboard/gwt/index.html
That link is now 404'd. Tried from several different browsers on different systems.
Registered under the new Google APIs Console already, but still get the problem.
// Set the authentication URL for this connection object
$authenticationURL= 'https://www.google.com/youtube/accounts/ClientLogin';
// Try to connect to YouTube with the channel credentials passed
try {
$httpClient =
Zend_Gdata_ClientLogin::getHttpClient(
$username = $channelfields['EMAIL_ADDRESS'],
$password = $channelfields['PASSCODE'],
$service = 'youtube',
$client = null,
$source = 'Redacted Data',
$loginToken = $channelfields['CACHED_TOKEN'],
$loginCaptcha = '',
$authenticationURL);
} catch (Zend_Gdata_App_HttpException $httpException) {
$update_error['response_body'] = $httpException->getRawResponseBody();
$update_error['error'] = 1;
} catch (Zend_Gdata_App_Exception $e) {
$update_error['message'] = $e->getMessage();
$update_error['error'] = 1;
}
This code has worked perfectly fine before, but does not work with the older API key, or the newer one generated inside the Google APIs console.
I'm attempting a simple upload and this concerns me greatly:
"The service account flow supports server-to-server interactions that do not access user information. However, the YouTube Data API does not support this flow. Since there is no way to link a Service Account to a YouTube account, attempts to authorize requests with this flow will generate a NoLinkedYouTubeAccount error."
From all reports it seems that Google has forced YouTube uploads to become interactive in all cases precluding all possibility of platforms that automatically upload generated content from working at all.
Any help or insights into the process is appreciated.
P.S - Ohhh, it's been awhile since I looked at that system and Google shut down the YouTube Developer Forums and said "YOU" were responsible for their support now :)
OAuth2 does support the ability to avoid user interaction through the offline access type parameter (ie, using access_type=offline). Check out Google documentation for details.
The solution is really rather simple. Your app needs to use oauth to request offline access. It will be given an access cide which you convert to a refresh token, which is the thing you store in your database. This doesn't expire. Well actually it sometimes does, but that's another story. Whenever you need to access the api, use the stored refresh token to request an access token which you include in each api call.
See https://developers.google.com/accounts/docs/OAuth2WebServer for details.
I don't know what you did but https://code.google.com/apis/youtube/dashboard/gwt/index.html works perfectly fine for me. Maybe it was a temporary issue. If you want no user interaction you HAVE to use YouTube API v2 OR you have to use v3 with methods that don't require authentification OR you have to provide your own youtube account credentials which is not recommended and probably not appropriate for you situation.
Several issues to respond here, I think.
1) The older API console has not been removed, but I've noticed intermittent outages to it and to the newer API console while Google is rolling out their new "cloud console."
2) ClientLogin was officially deprecated in April of 2012, not just 48 hours ago. Jeff Posnick has detailed all the changes over the months (and related ones, such as AuthSub, Youtube Direct, etc.) at his blog (apiblog.youtube.com).
3) You're right that, with v3 of the APIs, you cannot do automatic uploads across the board, as the oAuth2 flow requires user interaction. However, given the limited description of your use case, using refresh tokens is probably your best bet. If the content is user generated, somewhere they must be logging into your app, correct? (so that your app knows which credentials to leverage to do the uploads). At the point they're logging into your app, and you're starting the oAuth2 flow, you just have to hit the first oAuth endpoint and pass it the parameter access_type=offline (along with any other parameters). This will ensure that, when they grant that initial permission, you're returned a refresh token instead of an access token. With that refresh token, you can exchange it for multiple access tokens as needed (an access token lives for about an hour. I don't know how long a refresh token lives, but I've never had one expire before my own login cookies did, and then I just get a new one when my users re-login to my app).
Here's some more info on how to use the refresh token; note, too, that the various google api client libraries make it pretty smooth.
https://developers.google.com/accounts/docs/OAuth2WebServer#refresh
Also, this video tutorial from a Google Developers Live broadcast a couple of months ago might help illustrate the point: http://www.youtube.com/watch?v=hfWe1gPCnzc -- it's using the oAuth playground rather than a client library, but the concept is the same.
The answer is to use google-api-php-client, create an interactive auth page, and set up YouTube API v3 correctly with the new API console.
You can create a very simple page that will authenticate for the supplied channel and then store the correct token in your database. Is already working and uploading hundreds of videos on one channel. You do need to remember to fully activate yourself under the new API console and add the services required. Just keep authenticating and adding the services it says it needs. After that, the regular v3 upload process works just fine. On failure send a group an email and they can get a new token in 10 seconds.
Not the most elegant solution, but the documentation from Google is far from elegant anyways that Stack Overflow is now their front line support.
Just hang in there, a solution is always found. Don't give up!
I didn't get here by myself either, the other answers on this page helped me get all the way to this point. Thanks guys.
P.S - Don't forget the scopes
$client->setScopes("https://www.googleapis.com/auth/youtube https://www.googleapis.com/auth/youtube.upload");

How do I connect to the Google Calendar API without the oAuth authentication?

I have been studying the Google Calendar API and the docs on authentication (http://code.google.com/apis/calendar/v3/using.html#auth). It seems that the use case mentioned here is writing an application that accesses the user's calendar. However, I am writing a web page that will access the web site owner's calendar and display just that one calendar's information. So, I don't want the user to enter in their Google account information which is what oAuth wants to do.
Basically, I am looking for a way to access a single private Google calendar and authenticate to it by passing the credentials directly to the service.
There is a similar questions here: How to use OAuth with Google Calendar to access only ONE calendar?
that seems to indicate that the poster originally was passing the credentials directly. Is this feature still available? How do you handle the use case I described?
If I'm not wrong, they've launched Service Account for this now: https://developers.google.com/accounts/docs/OAuth2ServiceAccount
Edit:
Here's the modification from their Prediction API
session_start();
require_once "google-api-php-client/src/Google_Client.php";
require_once "google-api-php-client/src/contrib/Google_CalendarService.php";
const CLIENT_ID = '...';
const SERVICE_ACCOUNT_NAME = '...';
// Make sure you keep your key.p12 file in a secure location, and isn't
// readable by others.
const KEY_FILE = '...';
$client = new Google_Client();
$client->setApplicationName("...");
if (isset($_SESSION['token'])) {
$client->setAccessToken($_SESSION['token']);
}
// Load the key in PKCS 12 format (you need to download this from the
// Google API Console when the service account was created.
$key = file_get_contents(KEY_FILE);
$client->setAssertionCredentials(new Google_AssertionCredentials(
SERVICE_ACCOUNT_NAME,
array('https://www.googleapis.com/auth/calendar', "https://www.googleapis.com/auth/calendar.readonly"),
$key)
);
$client->setClientId(CLIENT_ID);
$service = new Google_CalendarService($client);
//Save token in session
if ($client->getAccessToken()) {
$_SESSION['token'] = $client->getAccessToken();
}
//And now you can use the code in their PHP examples, like: $service->events->listEvents(...)
I have the same problem. I found this 3 part tutorial that makes exactly what you want . Web page for bookings
You create a web page that writes directly into your own calendar (not the users) and that is why users don't have to give you permission to access their calendar.
This solution works for me, but you have to manage it directly with REST.
I am looking for a way to use google api php client because it seems a bit simpler. But I have not yet found a way to how to properly edit the google-api-php-client/src/config.php file to work in a way where you authenticate yourself not the actual user, because you need access to your calendar. If anyone has an idea or a script that would do that I would be very grateful.
Also take a look here:
https://github.com/googleapis/google-api-php-client#authentication-with-service-accounts
After creating a service account you can authenticate without the user prompt for adding items to a calendar your service owns.

Resources