Performing a Secure Multi-Part POST from within a Jive App

Version 1

    This was written by our brilliant Jive Engineer Edward Venaglia!

     

    The approach involves an old-school HTML form, and a few iFrames to get around the browser's cross domain security policies. It also requires you to run your app from the /public folder of an add-on zip.

     

     


     

    The Process

    Screen Shot 2016-07-14 at 9.02.40 AM.png

    1. We setup and submit the firm by invoking submitForm().
      1. We need to first obtain the Jive cross domain token from the Jive container.
      2. Next set the cross-domain token on the form's parameters.
      3. Calculate the Jive URL, and append the v3 path; this becomes the form's "action".
      4. Set the target of the form to the iframe we have added just for this purpose.
      5. Apply any extra form data, specific to our needs.
      6. Submit the form.
    2. The form is sent to Jive
    3. The response to loaded into the iframe named "uploadFrame".
    4. The script in "cross-domain-relay.html" is polling the iframe named "uploadFrame" and detects the newly loaded document.
    5. The body of the response is parsed and sent to the parent window (our app) using window.postMessage().
    6. Our message listener is invoked.
    7. After validation, our callback is invoked, passing the cross-domain response to the form post.

     


     

    Limitations

    • The app must be hosted using /public resources of the add-on.
    • This requires Jive 8.0 or Jive Cloud to use /public resources.
    • The JSON response content type does not work with older versions of IE. Test it first!

     


     

    The Code

    First we need to add the form tag:

    ... and any other form elements you need to build your POST

     

    Next we need some iFrames, one of which is the target of the form we just added:

     

    Now for some JavaScript to set the src of those iFrames:

    $('iframe[name="uploadFrame"]').attr('src', getAddonURL() + '/blank.html');
    $('iframe[name="cross-domain-relay"]').attr('src', getAddonURL() + '/cross-domainrelay.html?uploadFrame');
    function getAddonURL() {
         var matches = window.location.href.match(/.*?[?&]url=([^&]+)%2F.*$/);
         if (matches.length == 2) {
              return decodeURIComponent(matches[1]);
         } else {
              osapi.jive.core.container.sendNotification(
              { "severity" : "error", "message": "Could not parse addon URL from window location " + window.location.href }
              );
             return '';
         }
    }
    

     

    Responses are delivered by cross-domain message, so we need a handler:

    var postCallback;
    var originDomain = document.referrer.substring(0, document.referrer.indexOf('/', 8));
    window.addEventListener('message', function(event) {
         var eventData = JSON.parse(event.data);
         if (event.origin !== originDomain || !eventData || typeof(eventData['form-postcross-domain-relay']) === "undefined") {
              return;
         }
         console.log('Received data', eventData);
         var error = eventData['form-post-cross-domain-relay'].error;
         if ( error ) {
              console.error('error.statics.upload', ' ' + error.message);
              return;
         }
         if ( postCallback ) {
              postCallback(eventData['form-post-cross-domain-relay']);
         }
         // unwire the post callback; callers must set it again
         postCallback = null;
    });
    

     

    Submitting the form is done using JavaScript:

    function submitForm(v3uri, callback) {
         var $postForm = $("#j-post-form");
         osapi.jive.core.container.requestXJToken( {}, function(newToken) {
              setupToken(newToken, formDomID);
              var tokenName = newToken['jive.token.name'];
    
              $postForm.find('.j-form-token')
              .attr('name', tokenName)
              .val(newToken[tokenName]);
    
              $postForm.find('input[name=\'jive.token.name\']').val(tokenName);
    
              var jiveURL = getJiveURL();
              var actionURL = jiveURL + '/api/core/v3' + v3uri;
    
              $postForm
              .attr('action', actionURL )
              .attr('method', 'POST' )
              .attr('target', 'uploadFrame' )
              .removeAttr('onsubmit');
         
              // Set any other form post data you need to set before uploading
              postCallback = callback;
              $postForm.submit();
         });
    }
    function getJiveURL() {
         var href = getAddonURL();
         return href.substring(0, href.indexOf('/resources/add-ons'));
    }
    

     

    Finally, we add a few files to our add-on zip: (sources are attached)

    • blank.html
    • cross-domain-relay.html