These articles are abridged from my  book on Office to Apps migration.
Going GASfrom VBA to Google Apps Script.
Now available  directly from O’Reilly or Amazon.

So far all the authentication examples have been based on the normal Oauth2 flow. In the developers console, you probably noticed something called a Service Account. This is normally used by server based applications to identify themselves to co-operating applications, but they can also now be used by Apps Script. This became possible by the addition of these methods to the Utilities service.

Utilities.computeHmacSha256Signature()
Utilities.computeRsaSha256Signature()

These are needed to properly sign a JWT (JSON Web Token) in which service account credentials are passed to the authentication infrastructure. I’ll show how to use service accounts in a later post, but first it’s worth looking at what a JWT is and how it is constructed since they are used in many different systems and platforms. 

What is a JWT Token ?

The purpose of a JWT is to provide reassurance to ‘B’ that a message was indeed sent by the correct party (in this case ‘A’),  and that what was received by ‘B’ matches what ‘A’ intended to send.


It’s important to realize that this is not some kind of encrypted message that can be decrypted back to its original state, but rather the message  (encoded as Base64), and a digitally signed version (using a secret known to both A and B) version of the message is sent as part of the same JWT. B can sign the unsigned message portion using the shared secret and the same signing algorithm, and if the result equals the signed message, can have confidence the the message is valid.jw

Firebase authentication

As an example of creating a JWT from scratch, let’s look at how to generate a JWT for the Firebase noSQL database. One of the features of Firebase is to sign requests with the developer’s secret key. That way Firebase can know that the request has come from a valid source and has not been modified en route. 

JWT format

The structure of a JWT is

message.base64(signedmessage)

where message is 

base64(header).base64(claims)

In this case, the base64 encoding is the websafe variety with padding removed as is required by the JWT specification. The claims is the object describing containing the assertions  the sending party wishes to make and the header describes the algorithm and structure of the JWT itself. 

message components

component

property

purpose

header

alg:”HS256”

the encryption method (the google service account uses RS256)

 

typ:”JWT”

identifies this as a JWT

claims

iat:Math.floor(new Date().getTime()/1000)

the time now in seconds

 

v:0

the version number

 

d:{uid:”bruce”}

any data to be passed to the security and rules processing on Firebase. This example is passing a user id.

 

Firebase expects the d property in the claim to contain data which is accessible in the ‘security and rules’ section of the Firebase dashboard. Typically this would be used to react in different ways depending on the passed user id. A generated token looks like this (the message components are separated by ‘.’)

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkIjp7InVpZCI6ImJydWNlIn0sImlhdCI6MTQ0MTY0NDgyNiwidiI6MH0.xiqtUhmupdwzvhkmmkK_lnx2joenPLxG738qlZqiS9M

and is passed to Firebase to prove the authenticity of the accompanying request.

Code

This small module generates a JWT from your Firebase secret as follows, where data is the object you want to communicate to the Firebase security and rules processing, and secret is your Firebase developer secret (which you should store securely in the PropertiesService).

var token = FirebaseAuth.generateJWT (data, secret);

In a future post, I’ll publish a JSON library for Apps Script you can use with this to access FireBase database.

FirebaseAuth code

var FirebaseAuth = (function (firebaseAuth) {
  /**
   * generate a jwt for firebase using default settings
   * @param {object} data the data package
   * @param {string} secret the jwt secret
   */
  firebaseAuth.generateJWT = function(data, secret) {
    var header = getHeader_ ();
    var claims = getClaims_ (data);
    var jwt = header + "." + claims;
    // now sign it 
    var signature = Utilities.computeHmacSha256Signature (jwt, secret);
    var signed = unPad_ (Utilities.base64EncodeWebSafe(signature));
    // and thats the jwt
    return jwt + "." + signed;
  };
  /**
   * generate a jwt header
   * return {string} a jwt header b64
   */
  function getHeader_ () {
    return unPad_(Utilities.base64EncodeWebSafe(JSON.stringify( {
      "alg": "HS256",
      "typ": "JWT" 
    })));
  }
  /**
   * generate a jwt claim for firebase
   * return {string} a jwt claimsm payload b64
   */
  function getClaims_  (data) {
    return unPad_ (Utilities.base64EncodeWebSafe( JSON.stringify( {
      "d" : data || {},
      "iat": Math.floor(new Date().getTime()/1000),
      "v": 0
    })));
  }
  /**
   * remove padding from base 64
   * @param {string} b64 the encoded string
   * @return {string} padding removed
   */
  function unPad_ (b64) {
    return b64 ?  b64.split ("=")[0] : b64;
  }
  return firebaseAuth;
})(FirebaseAuth || {});

For help and more information join our community,  follow the blog or follow me on Twitter.