I've solved this in the following way, which seems to cover most bases. I've assumed that my domain is enabled for customers, because otherwise lightning components are not usable.
The main issue I think you are having is this critical update, which removes the pod from visualforce (and other) hostnames, and is responsible for the change from visual.force.com
to visualforce.com
.
To handle this, you have two options:
Instruct all of your customers to enable the critical update (which
will be done 19/10/2019 anyway.)
Generate both the old and new format hostnames, send messages to both, and check messages coming in are from one or the other. To be very clear - I've not reviewed this approach with Salesforce's security review team - you might want to validate it with them, but in my mind it's sane because both domains will contain the same pages under package control.
Posting a message to a hostname which is not present in the iframe presents a message in the console in chrome, but no errors.
public ProxyController() {
String hostname = Url.getSalesforceBaseUrl().getHost();
String myDomain = hostname.split('\\.')[0];
if(myDomain.contains('--')) {
//uses lastIndex to exclude namespace but include sandbox names
// e.g. https://mydomain--sandbox--namespace.visualforce.com
// and https://mydomain--namespace.visualforce.com
myDomain = myDomain.substring(0, hostname.lastIndexOf('--'));
}
lcBaseURL = 'https://' + myDomain + '.lightning.force.com';
Map<String, String> headers = ApexPages.currentPage().getHeaders();
headers.put('X-Frame-Options', 'ALLOW-FROM ' + lcBaseURL);
headers.put('Content-Security-Policy', 'frame-ancestors ' + lcBaseURL);
}
@AuraEnabled
public static List<String> getVFBaseURL() {
String hostname = Url.getSalesforceBaseUrl().getHost();
// will support prod and sandboxes
// e.g. https://mydomain--sandbox.lightning.force.com
// and https://mydomain.lightning.force.com
String myDomain = hostname.split('\\.')[0];
String namespace = ProxyController.class.getName().split('\\.')[0];
String pod = [SELECT InstanceName FROM Organization].InstanceName.toLowerCase();
return new List<String>{
'https://' + myDomain + '--' + namespace + '.visualforce.com',
'https://' + myDomain + '--' + namespace + '.' + pod + '.visual.force.com'
};
}
One warning - we could not get iframing to work reliably in Safari or Salesforce Mobile on ios, so did not use this mechanism in our managed package in the end. I've posted a question about it, but not made much progress in understanding the problem nor resolving it. This approach worked successfully for us in Chrome / Edge / Firefox and Salesforce Mobile on Android.