Start programming Node.js http2 client mode (part II): use POST method to get access_token from google’s token server

Tomas • Y • C
6 min readNov 2, 2017

--

from last post we talked about how to request google’s cloud service via REST API calls, but to avoid article too long, I used borrowed token from gcloud auth application-default print-access-token however it’s valid for one hour only, if we want our program to run automatically and continuously, we can’t rely on that manual step to get tokens, so can we do this automatically?

The answer is yes! Just because the @google-cloud/bigquerypackage can do that, our code should also have a way to request token automatically! we can simply call whatever npm library the bigquery package is calling, that’s one way, but in this tutorial, since I know Google’s token service API also support http2, and non of existing npm library is utilizing http2 client yet, let’s make another piece of http2 client code to request token automatically!

Another reason is from the current bigquery library dependency analysis, there are 99 npm packages of a total size of 16MB plain text javascript code! do the bigquery library really need all of them? possibly, but we can always look into details if there’s any bloated code and let’s see if we can make a zero-dependency program to get token?

Image from NPM dependency visualization: npm.anvaka.com

from the dependency graph and reading each module’s actual code under ./node_modules/... we can figure out this dependency chain:

@google-cloud/biguery =>
@google-cloud/common =>
google-auto-auth =>
google-auth-library =>
gtoken =>
jws
request

this is simplified to show auth related dependencies; since request is mostly commonly used http client library, we can reasonably assume that gtoken is doing the actual heavy lifting work to call request to call google token service API and pass back tokens, if you’re interested you may go further to read its source code.

I have read it somewhat and can declare the answer is YES! we can make a zero dependency request token through http2 client mode.

Hint: doing automatically request gtoken is essentially to implement a server side web apps, to Google Developer’s guide definition, I have read so, if you want to skip that detailed document, I can simply tell you there are two kinds of role to request token for: either via a logged-in user’s refresh_token, or a service account; here we can start with simple one first:

The Logged-in user

at developer machine working with Gcloud project, usually we should have the google-cloud-sdk installed, that’s where gcloud command is provided; and with that we can login with a personal project id, e.g. a personal @gmail.com email id, or an organization email id if your organization is already with Google apps suite, with enterprise gmail; with either one you can login via gcloud auth application-default login to enable many other CLI tools from the google-cloud-sdk package, like gsutil, bq, and many others.

After the user is logged in, the login command will generate a refresh token saved at user’s home directory; the full path is ~/.config/gcloud/application_default_credentials.json its content is valid JSON looks like this:

{
"client_id": "764086051850-6xxxxx...apps.googleusercontent.com",
"client_secret": "a-long-secret...",
"refresh_token": "the-refresh-token................",
"type": "authorized_user"
}

from above developer guide I know that request token is just to send a POST request to its API endpoint, so here the only question is how to make a POST request under http2 client?

Let’s read this piece of code:

  1. all the require lines are nodejs builtin modules, hence it’s zero external dependency;
  2. since nodejs version 8 we have util.promisify can turn a nodejs style last parameter as callback(err, value) API to be returning a Promise! this way integrates better with async await calls;
    there are multiple libraries on npm registry doing the same thing, but why not use a built-in function if you’re on nodejs version 8 above?
  3. the getApplicationDefaultCredentials function is declared as an async function, to be able to use await inside; the async function is always returning a Promise, here we return by JSON.parse’ed object, it will be become a Promise.resolve(…) that eventually resolve to the value wrapped; but one extra benefit is if JSON.parse failed to parse it, it will become a rejected Promise, like Promise.reject(...) we can catch that in the outer loop;
  4. we define a GoogleToken class, this is Nodejs ES6 syntax, more similar to C++/Java/Python and others if you have other programming language background, much more intuitive than ES5 prototype based class definition, so consider upgrade Nodejs v6 or v8+; even if you can’t, you may still write ES6 code and use babel to transpile;
  5. the GoogleToken has a constructor and getToken method and expired read only attribute; the longest one is getToken function which has both fast path or slow path; if called for 2nd time 3rd time … and there’s a token that didn’t expire, just return it;
    if not, start the http2 client to request a new one;
  6. the request token code is simply a POST request under http2 client mode, just read theclient_id, client_secret, refresh_token from the default credential file, and pass them to a form post body, which is x-www-form-urlencoded formatted code, the builtin module querystring can do that work nicely!
  7. in the http2 client request, the difference with last writing is the :method and content-type in the request headers, http2 use all headers in lower case, if you don’t, the code will convert to lower case automatically but it’s still better to just use all lowercase; values of each header can still be mixed cases;
    in this case, it’s also better to set content-length in the headers but the http2 module is aware of that will add that if missing in the post mode; from last writing we didn’t set the :method default is GET; if we set to POST method, the module does more sanity check and we need send to request with a post body.
  8. in the ‘response’ callback; we add checks of the response headers, it should have :status and content-type should be json; notice here the checking of status code is via strictly equal to number 200, it’s not same as http1 was a string '200' the default == equality test would work here but not recommended; somewhere has a best practice about JavaScript equality test saying you should always use strictly equal test, except a few cases you know what you’re doing, like ==null is useful to check both null and undefined;
    but here need to notice the status code difference in http2 and http1.
  9. the async main function and module run as main script, as await keyword can only be used in an async function, the code can not be within a code section in global scope, we have to make it in an async function to call await; intuitively can be named as main and theif (require.main === module) test is a nice way to make sure it only runs when the script is run as main script;
    PS: with a long time Python background, I have written many Python script with if __name__ == ‘__main__’: main()can be either a CLI to be something useful, or be a library code to be import by others; now with this main code run test, a nodejs script can archive similar behavior

run this script:

$ ~/opt/node-v9.0.0-linux-x64/bin/node ./gtoken.js
(node:2420) ExperimentalWarning: The http2 module is an experimental API.
ya29.Glv3BEmZYyz331jPAuoLwU2Xy9xxxxxxxxxx........q4MFouu82gNCHTEpsbS

Summary:

in this post we showed that a simple http2 client to request access token from google token service; the post method is very similar to http1 post method, with only a few differences mentioned here;
next post will be with service account, that’s a little more complicated.

want to know more on http2 client mode? or Nodejs v9, please clap or share, let me know by any way.

--

--

Tomas • Y • C

Open Source Evangelist | Early Git advocator since 2007 | Node.js user since ES2015