5

I'm doing a PoC with canvas. The connected app is an instance of PeopleSoft. The integration is done but with some difficulties. I'm getting a cross-domain error in the browser when some of the PeopleSoft's JavaScript libraries are trying to access window.top...

Below is an extract of the headers sent to PeopleSoft by the canvas frame.

Request headers:

POST /psc/xxxxxxx/EMPLOYEE/HRMS/s/WEBLIB_ACN.ISCRIPT1.FieldFormula.IScript_SFDCGW 

HTTP/1.1
Host: xxxx.peoplesoft.xxxxxxxx.com
Connection: keep-alive
Content-Length: 4090
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: https://c.na16.visual.force.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36
Content-Type: application/x-www-form-urlencoded
DNT: 1
Referer: https://c.na16.visual.force.com/servlet/servlet.Integration?lid=1111111111111&ic=1
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8,es;q=0.6
Cookie: INFAESAD0205-8100-PORTAL-PSJSESSIONID=xxxxxxxxxx!11111111; ExpirePage=https://xxxx.peoplesoft.xxxxxxxx.com/psc/xxxxxxx/; PS_LOGINLIST=https://xxxx.peoplesoft.xxxxxxxx.com/xxxxxxx; PS_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxx+xxxxxxxxxx==; SignOnDefault=; PS_DEVICEFEATURES=width:1366 height:768 pixelratio:1 touch:0 geolocation:1 websockets:1 webworkers:1 datepicker:1 dtpicker:1 timepicker:1 dnd:1 sessionstorage:1 localstorage:1 history:1 canvas:1 svg:1 postmessage:1 hc:0; PS_TOKENEXPIRE=26_May_2015_12:37:26_GMT

Response Header:

Access-Control-Allow-Credentials:true
Access-Control-Allow-Origin:https://c.na16.visual.force.com
Cache-Control:no-cache
Cache-Control:no-store
Content-Encoding:gzip
Content-Length:727
Content-Type:text/html; CHARSET=UTF-8
Date:Tue, 26 May 2015 12:37:53 GMT
Expires:Thu, 01 Dec 1994 16:00:00 GMT
IgnorePortalRegisteredURL:1
PortalRegisteredURL:https://xxxx.peoplesoft.xxxxxxxx.com/psc/xxx/EMPLOYEE/HRMS/s/WEBLIB_ACN.ISCRIPT1.FieldFormula.IScript_SFDCGW
Set-Cookie:PS_TOKENEXPIRE=26_May_2015_12:37:53_GMT; path=/; secure
UsesPortalRelativeURL:true

Normally I can do this within my connected app:

top.location.href = '/xxxxx';

But in this case I am getting this error:

Uncaught DOMException: Blocked a frame with origin "https://xxxx.peoplesoft.xxxxxxxx.com" from accessing a cross-origin frame.

This is most likely a PeopleSoft-related question but I'm trying to understand how is Salesforce able to allow cross domain access for connected apps (apart from the Canvas js lib).

Thanks!

  • Check the html generated from your page, and look at what is contained in a frame, chances are the source js is being hosted inside a frame, or is in some location which is hosted on a different domain. – battery.cord May 26 '15 at 13:33
  • It doesn't look like it does that. The js libs are referenced with a relative path: <script language='JavaScript' type='text/javascript' src='/cs/xxxxxx/cache/PT_COMMON_MIN_40.js'> </script> – dangt85 May 26 '15 at 13:42
11

This is a long answer that attempts to explain exactly what's going on here, and how HTML5 and the Force.com Canvas SDK allow safe cross-origin access. It may be that your issue has no solution, but at least you'll understand why!

The same-origin policy restricts how a document or script loaded from one origin can interact with a resource from another origin. Two resources have the same origin if the protocol, port (if one is specified), and host are the same for both resources. The policy is in place to help prevent attacks such as cross-site scripting (XSS), where JavaScript in one browser window might attempt to manipulate the DOM, or call JavaScript functions in another window.

One of the restrictions of same-origin policy is that JavaScript in an iframe is unable to manipulate the parent frame, if the two have different origins (this is specified by section 5.5.3.1 of the HTML5 standard).

So, how does Salesforce allow cross-origin access?

One mechanism is Cross-Origin Resource Sharing (CORS), which allows JavaScript loaded in a page in one domain, say https://myapp.herokuapp.com:443, to access the Force.com REST API at, say, https://na1.salesforce.com:443. To configure this, go to Setup | Security Controls | CORS and add a Whitelisted Origin for your server.

In this instance, however, CORS isn't going to help you, since you're trying to manipulate the parent frame href, rather than call an API.

A more relevant technology is HTML5's window.postMessage. The Salesforce Canvas SDK uses this to make calls from code in an iframe to code in the parent frame. postMessage allows you to register a handler in one frame, to which you can post messages from another frame.

If you are in control of both the parent and child frames, you could do something like this:

In the parent frame:

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event)
{
  // check for the origin of the child frame
  if (event.origin !== "https://xxxx.peoplesoft.xxxxxxxx.com")
    return;

  // event.data holds the message payload
  // here we expect a URL and simply go to that location
  // you can pass a JavaScript object for more complex cases
  window.location.href = event.data;
}

In the child frame:

// First argument is message payload - the URL we want to go to
// Second argument is the origin of the top frame
top.postMessage('/xxxxx', 'https://c.na16.visual.force.com');

However, since you're using Canvas, the Canvas SDK is also using window.postMessage, so it's not a good idea to install your own message handler. Helpfully, the Canvas SDK provides a mechanism you can use to make calls from JavaScript in the Canvas app to the parent Visualforce page - you can publish and subscribe to events.

In the parent Visualforce page:

Sfdc.canvas.controller.subscribe({name : 'mynamespace.loadUrl', 
                                  onData : receiveEvent});

function receiveEvent(payload)
{
  // function argument holds the message payload
  // again, we expect a URL and simply go to that location
  // you can pass a JavaScript object for more complex cases
  window.location.href = payload;
}

In the child Canvas app:

Sfdc.canvas.controller.publish({name : 'mynamespace.loadUrl',
                                payload : '/xxxxx'});

Now this assumes you have access to the code in the child iframe to make the cross origin call in this way. If you don't, and the child iframe is running some third-party code that's trying to do

top.location.href = '/xxxxx';

then I don't think you'll be able to make this work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.