UPDATE: Added reference to another blog post that does a great job of going into the cross-document / cross-domain issue. Fixed some grammar/typos.
I’ve spoken to some folks recently who built solutions using CRM 4.0 and were fond of the ability to put custom ASP.NET (aspx) pages in the ISV folder. As of CRM 2011, this is a deprecated feature. This has caused a bit of heartache and frustration with those fond of the feature. The CRM 2011 SDK provides guidance on how to address this in the following sections:
What you’ll find in the guidance is a new approach that works similarly to the ISV folder approach. The major difference is that your custom ASP.NET pages run in a separate web site, hence a separate IIS Application Pool. The forced decoupling of your code executing in the same context as the CRM Server code is a good thing in my opinion. This is very similar to how plugins run in isolation. By allowing your ASP.NET code to run in-process with the CRM Server code, the 4.0 approach was at the mercy of poorly written code. Your code could actually adversely effect the CRM UI. As I understand it, this happened quite a bit in the 4.0 days. Isolation of your code from CRM code is more resilient. Although probably less often used, an added benefit of this separation of is that you have another boundary that can be scaled independently. Depending on the nature of your code, there may be value in running your stuff on their own servers.
The issue surfaces when your companion site is running in a different domain (or even subdomain). Browsers will surface a security error and prevent the call from happening. Why would you run the companion site in a different domain/subdomain? Here are a few reasons:
- You use CRM Online and the companion site is in Windows Azure, Office 365, or any hosting provider for that matter
- You are using claims based authentication with an internet facing deployment (IFD)
- If you are using SSL for your CRM deployment, then you must use SSL for your companion site which means you can’t differentiate the sites by port because they both require 443. BTW, you can use wildcard certificates and host headers to bind to 443 multiple times on the same server.
So what’s the issue? Well you can’t call window.parent unless both pages share the same domain of origin. Now if you are web savvy, you might say “aha, but I can use document.domain on both ends!” And you would be justified in assuming you could. However there are some issues:
- document.domain only works for sites that are subdomains of the primary domain (ex: a.domain.com and b.domain.com)
- document.domain won’t work for child pages hosted in Windows Azure, SharePoint Online, or anywhere else that’s not in the domain of your CRM deployment
- Most importantly, using document.domain has an adverse side effect that causes CRM 2011 functionality to fail in certain scenarios
It’s important to understand that the cross domain issue has nothing to do with CRM 2011. It has everything to do with how browsers work. As a security measure, they don’t allow you to make cross domain calls. One could argue that you didn’t have to deal with this when the ISV folder was an option. Fair enough, but I’ve already made my case for why the deprecation of the ISV folder was a good thing. If you choose to ignore the fact that the ISV folder has been deprecated, you put yourself or your customer in jeopardy for upgradeability in the future. Don’t to it. Period.
Ok, enough background…how do you make this work if you absolutely must have window.parent.someFunction like interaction from a child page in another domain and a CRM form? There’s an API in modern browsers called window.postMessage. Not all browsers support window.postMessage. However, there’s a handy little library that wraps window.postMessage and includes a fallback mechanism for legacy browsers:
In my tests, I’ve been able to successfully use this approach across a number of scenarios. I’ve created a sample of the approach in action. Here are the instructions to get it working:
- Download the two zip files from my SkyDrive here.
- Unzip the CrossDomainPostMessage.zip file
- Copy the contents to the root of your companion web site
- Import the CrossDomainMessaging_1_0_0_0.zip into your CRM 2011 organization.
- Make sure you go to the “Cross Domain Messaging” entity configuration page after import and check the Workplace checkbox so it shows up in the left navigation.
- Customize the “Cross Domain Messaging” form.
- Configure the form properties.
- Edit the form onload handler configuration.
- In the following example, make sure you replace any reference to https://child.yourdomain.comwith your own your root url:
- Change the value in the parameter box from "http://child.devkeydet.com/CrossDomainPostMessage.htm" to “https://child.yourdomain.com/CrossDomainPostMessage.htm”
- Change the properties of the IFRAME on the form setting the URL value to https://child.yourdomain.com/blank.htm.
If I’ve explained that right, you should have the sample working. The instructions are purposefully vague so they can be applied to the various scenarios this can run in.
There’s another helper library called easyXDM that’s similar to the jQuery postMessage plugin I use in the sample. They both use the same basic approach. I chose jQuery postMessage because it was lighter weight and got the job done. easyXDM is more feature rich and worth looking at as well. There’s a great article here that talks about this whole scenario in even more detail, outside of the CRM context. It also mentions easyXDM at the end.
What’s the lesson here? Don’t use window.parent. Instead, use window.postMessage or the helper library I use in my samples. By doing this, you have a solution that will work in all the major combinations of possible CRM 2011 deployment environments:
- on-prem with windows authentication
- on-prem with claims based authentication
- IFD with claims based authentication
- CRM Online with Office 365
- CRM Online with Windows Azure
- Pretty much anything that has SSO configured