docs: function authenticateAppApiKeyAgainstAuthorisedProjects(cb) {
      var sentApiKey = getAppApiKey();
  
      // Fail fast if no sent api key
      if (!sentApiKey) {
        return cb({
          code: UNAUTHORISED_HTTP_CODE,
          message: "Missing App API key(s)"
        });
      }
  
      var millicore = process.env.FH_MILLICORE;
      var millicoreProt = process.env.FH_MILLICORE_PROTOCOL || "https";
      var env = process.env.FH_ENV;
      var project_id = process.env.FH_WIDGET;
      var app_id = process.env.FH_INSTANCE;
  
      var now = new Date().getTime();
  
      //check cache
      if (authenticateAppApiKeyAuthProjCache.hasOwnProperty(sentApiKey)) {
        var cache = authenticateAppApiKeyAuthProjCache[sentApiKey];
        //cache not timed out return cb else auth again
        if (cache.timeout > now) {
          // Cache hit
          if (cache.auth) {
            return cb();
          } else {
            return cb({
              code: UNAUTHORISED_HTTP_CODE,
              message: cache.message
            });
          }
        } else {
          // Cache expired - remove and continue
          delete authenticateAppApiKeyAuthProjCache[sentApiKey];
        }
      }
  
      var data = {
        environment: env,
        appApiKey: apiKey,
        clientApiKey: sentApiKey
      };
  
      // Call Core and validate passed key
      var url = millicoreProt + "://" + millicore + "/box/api/projects/" + project_id + "/apps/" + app_id + "/validate_key_against_authorised_projects";
      request.post({
        url: url,
        json: true,
        body: data
      }, function(err, res) {
        // Can set API_KEY_VALIDATION_TIMEOUT to expire more frequently than 5 minutes
        var expiration = parseInt(process.env.API_KEY_AUTH_PROJ_VALIDATION_TIMEOUT, 10) || 300000; // 5 minutes
        var cacheLength = new Date().getTime() + expiration; // 5 minutes
  
        // Explicitly handle 404s from Millicore here - the validate_key_against_authoried_projects endpoint may not exist in this cluster
        // If we get a 404, continue
        if (res && res.statusCode === 404) {
          authenticateAppApiKeyAuthProjCache[sentApiKey] = {
            "timeout": cacheLength
          };
          return cb();
        }
  
        if (err) {
          return cb({
            code: 500,
            message: "Error talking to Core to validate API Key - please try again",
            details: err.message
          });
        }
  
        if (res.statusCode === 200) {
          authenticateAppApiKeyAuthProjCache[sentApiKey] = {
            "timeout": cacheLength,
            "auth": true,
            "message": ""
          };
          return cb();
        } else {
          var errorMsg = (res.body && res.body.message) || "Invalid API Key";
          console.log("Issue validating app api key. Status Code: " + res.statusCode + ". Error Message: " + errorMsg);
  
          authenticateAppApiKeyAuthProjCache[sentApiKey] = {
            "timeout": cacheLength,
            "auth": false,
            "message": errorMsg
          };
  
          return cb({
            code: UNAUTHORISED_HTTP_CODE,
            message: errorMsg
          });
        }
      });
    }