5

I'm trying to use the YouTube Data API V3 with Node, in an attempt to provide an authenticated proxy around doing video searches. As the service is going to run on a server, I've created a service account.

This results in the download of a key file and the following information:

I have a very basic understanding of JWTs, but the service section on the OAuth2 page suggested that you should be using one of their libraries to do it, rather than implementing it yourself. However, they don't list a Node library in their supported libraries.

A little more digging and I came across the Google API Nodejs client: https://github.com/google/google-api-nodejs-client which claims to be supported by Google and has OAuth2 support out of the box.

The docs are fairly minimal and the examples for authentication involves printing a URL into a terminal and then visiting it in a browser to generate a token. I had a dig around the source to see whether it supported JWT and it does appear to have a JWTClient built in.

/**
 * @constructor
 * JWT service account credentials.
 *
 * Retrieve access token using gapitoken.
 *
 * @param {string=} email service account email address.
 * @param {string=} keyFile path to private key file.
 * @param {array=} scopes list of requested scopes.
 * @param {string=} subject impersonated account's email address.
 *
 */
function JWT(email, keyFile, key, scopes, subject) {
 JWT.super_.call(this);
 this.email = email;
 this.subject = subject;
 this.keyFile = keyFile;
 this.key = key;
 this.scopes = scopes;
 this.GAPI = GAPI;
}

This doc comment above the constructor seems to be the only explanation here and I'm still unsure of how to use this function.

I'm assuming:

  • email is the email address of your developer account
  • keyFile is the path to the private key
  • key is the private key? (If so, why include path as well?)
  • scopes is an array of API scopes you want to access
  • subject ???

Has anyone used this service before, or be able to shed a little light on the discrepancies here?

Just as a reference, this has to be server side as I need the authentication for requests to be autonomous and I can't just use non-authenticated requests as I exceed my daily allowance in 200 requests (or something like that).

asked Apr 20, 2014 at 11:08

2 Answers 2

14

I've managed to piece together a working solution, so I'll leave it here for anyone else who has a similar problem in the future.

There is a JWT example in the repo, which shows in more detail how you use the constructor for the JWT object.

https://github.com/google/google-api-nodejs-client/blob/master/examples/jwt.js

Here's a slightly amended version of the doc comment.

/**
 * @constructor
 * JWT service account credentials.
 *
 * Retrieve access token using gapitoken.
 *
 * @param {string=} email service account email address from developer console.
 * @param {string=} keyFile absolute path to private key file.
 * @param {string=} key the contents of the key if you are loading it yourself (optional)
 * @param {array=} scopes list of requested scopes.
 * @param {string=} subject impersonated account's email address (optional).
 *
 */

And the finished code looks like this

// Create the JWT client
var authClient = new googleapis.auth.JWT(email, keypath, key,
 ['https://www.googleapis.com/auth/youtube']);
// Authorize it to produce an access token
authClient.authorize(function(err, tokens) {
 if(err) throw err;
 // Use discovery to get the youtube api
 googleapis.discover('youtube', 'v3')
 .execute(function(err, client) {
 // Create a search
 var search = client.youtube.search.list({
 q: '<query string>',
 part: 'snippet',
 maxResults: 50
 });
 // Authenticate with current auth client
 search.authClient = authClient;
 // Hey presto!
 search.execute(function(err, results) {
 if(err) throw err;
 console.log(results.items);
 });
 });
});

This code example is a mess, but it's enough to give you the idea. Based on the example you should be able to do:

client.youtube.search.list({
 ... 
})
.withAuth(authClient)
.execute(...); 

But for some reason the withAuth method isn't there. It took a bit of digging to work out what it did and as far as I can see, it works fine if you set the authClient property by hand, as shown above.

As a side note, the err arguments are not errors, but POJOs and throwing them as above doesn't actually work. Unless you're happy to see [Object object] as your debug info.

Hopefully that library will get some proper documentation attention soon and problems like this will be ironed out.

answered Apr 20, 2014 at 13:56
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the example. I'm running into an issue if I don't provide a key, it treats the scopes argument as keys then leaves scopes blank. Were you just omitting keys or am I totally missing something?
I had a similar problem at one stage, the docs seem to suggest you can either pass the key in plaintext as an argument, or alternatively, the path to your keyfile. It doesn't explain how though and omitting either seemed to give me an error. I ended up passing both arguments and it worked (hacky, I know).
authClient = new googleapis.auth.JWT(config.jwt.email, config.jwt.keyPath, config.jwt.key, [https://www.googleapis.com/auth/youtube']);
3

The sample above seams to be outdated, so I attach what I found out. There is no good documentation apart from intellisense for the library. I am using pubsub in my example:

const {google} = require('googleapis');
const path = require('path');
const util = require('util')
async function runSample () {
 // Create a new JWT client using the key file downloaded from the Google Developer Console
 const authClient = await google.auth.getClient({
 keyFile: path.join(__dirname, 'jwt.keys.json'),
 scopes: 'https://www.googleapis.com/auth/pubsub'
 });
 // Obtain a new pubsub client, making sure you pass along the authClient
 const pb = google.pubsub(
 {
 version:'v1',
 auth: authClient
 });
 //make a request to list subscriptions
 //the returned object is a axios response object
 const res = await pb.projects.subscriptions.list(
 {
 project: 'projects/my-project'
 });
 //return response data
 return res.data;
}
runSample().then(res =>{
 console.log(util.inspect(res, false, null))
}).catch(console.error);
answered Aug 8, 2018 at 5:48

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.