Cloud Run Script

February 12, 2023

/

3 min read

--- views

--- comments

Last Updated February 12, 2023

Prepare

  1. Generate and download a service account JSON key file.
  2. Copy and paste the contents of the JSON key file into the serviceAccountKey environment variable in a Postman environment.
  3. Set the baseUrl collection variable in a Postman collection to the URL of your Cloud Run service.
  4. Set the type of Authorization to Bearer Token and the value to {{idToken}} in the Authorization tab of the Postman collection or request.
  5. Copy and paste the contents of the pre-request-script.js file into the Pre-request Script tab of a Postman collection or request.

pre-request-script.js

Copy
const ENV_SERVICE_ACCOUNT_KEY = 'serviceAccountKey';
const ENV_JS_RSA_SIGN = 'jsrsasign';
const ENV_TOKEN_EXPIRES_AT = 'tokenExpiresAt';
const ENV_ID_TOKEN = 'idToken';

const VAR_SERVICE_URL = 'baseUrl'

const JS_RSA_SIGN_SRC = 'https://kjur.github.io/jsrsasign/jsrsasign-latest-all-min.js';
const GOOGLE_OAUTH = 'https://www.googleapis.com/oauth2/v4/token';

const EXPIRES_MARGIN = 300; // seconds before expiration

const getEnv = name =>
  pm.environment.get(name);

const setEnv = (name, value) =>
  pm.environment.set(name, value);

const getJWS = callback => {
  // workaround for compatibility with jsrsasign
  const navigator = {};
  const window = {};

  let jsrsasign = getEnv(ENV_JS_RSA_SIGN);
  if (jsrsasign) {
      eval(jsrsasign);
      return callback(null, KJUR.jws.JWS);
  }

  pm.sendRequest(JS_RSA_SIGN_SRC, (err, res) => {
      if (err) return callback(err);

      jsrsasign = res.text();
      setEnv(ENV_JS_RSA_SIGN, jsrsasign);
      eval(jsrsasign);
      callback(null, KJUR.jws.JWS);
  });
};

const getJwt = ({ client_email, private_key }, serviceURL, iat, callback) => {
  getJWS((err, JWS) => {
      if (err) return callback(err);

      const header = {
          typ: 'JWT',
          alg: 'RS256',
      };

      const exp = iat + 3600;
      const payload = {
          aud: GOOGLE_OAUTH,
          iss: client_email,
          sub: client_email,
          iat,
          exp,
          target_audience: serviceURL
      };

      const jwt = JWS.sign(null, header, payload, private_key);
      callback(null, jwt, exp);
  });
};

const getToken = (serviceAccountKey, serviceURL, callback) => {
  const now = Math.floor(Date.now() / 1000);
  if (now + EXPIRES_MARGIN < pm.collectionVariables.get(ENV_TOKEN_EXPIRES_AT)) {
      return callback();
  }

  getJwt(serviceAccountKey, serviceURL, now, (err, jwt, exp) => {
      if (err) return callback(err);

      const req = {
          url: GOOGLE_OAUTH,
          method: 'POST',
          header: {
              'Content-Type': 'application/x-www-form-urlencoded',
          },
          body: {
            mode: 'urlencoded',
            urlencoded: [{
                key: 'grant_type',
                value: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
            },{
                key: 'assertion',
                value: jwt,
            }],
          },
      };

      pm.sendRequest(req, (err, res) => {
          if (err) return callback(err);

          const idToken = res.json().id_token;
          pm.collectionVariables.set(ENV_ID_TOKEN, idToken);
          pm.collectionVariables.set(ENV_TOKEN_EXPIRES_AT, exp);
          callback();
      });
  });
};

const getServiceAccountKey = callback => {
  try {
      const keyMaterial = getEnv(ENV_SERVICE_ACCOUNT_KEY);
      const serviceAccountKey = JSON.parse(keyMaterial);
      const serviceURL = pm.collectionVariables.get(VAR_SERVICE_URL);
      callback(null, serviceAccountKey, serviceURL);
  } catch (err) {
      callback(err);
  }
};

getServiceAccountKey((err, serviceAccountKey, serviceURL) => {
  if (err) throw err;

  getToken(serviceAccountKey, serviceURL, err => {
      if (err) throw err;
  });
});

Usage

Send the request and the idToken and tokenExpiresAt collection variables will automatically generated and cached in the collection. The idToken will re-generate when it expires.

References