Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Service Authentication with Google APIs in Nodejs

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).

like image 649
Dan Prince Avatar asked Apr 20 '14 11:04

Dan Prince


People also ask

How do I authenticate my Google API?

With a user account, you can authenticate to Google APIs and services in the following ways: Use the gcloud CLI to set up Application Default Credentials (ADC). Use the gcloud CLI to generate access tokens. Use your user credentials to impersonate a service account.


1 Answers

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.

like image 61
Dan Prince Avatar answered Sep 25 '22 10:09

Dan Prince