iOS third-party cookie saga with Tableau Server and Salesforce Mobile Publisher
TLDR: Salesforce Mobile Publisher for iOS won’t be able to run Tableau Visualizations embedded due to an iOS bug blocking third-party authentication cookies. This post has a solution for Tableau Server.
Since Apple’s introduction of Intelligent Tracking Protection, developers of iOS apps have struggled to integrate third-party components to their apps, because of a bug in the webkit engine that prevents third-party cookies. Everything works well in iOS Safari, so one can only assume it has special treatment (Safari uses a different webkit engine that allows third-party cookies when end-users disable cross-site tracking protection). On the other hand, apps built with WKWebView will struggle, even when the setting to “Enable cross-site tracking” is on. It’s a bug, because iOS ignores this setting. There’s really no good solution and only Apple can fix this.
This bug is also affecting Salesforce customers wishing to deploy an iOS mobile app for their Salesforce Experience Cloud (Professional, Partner or Community Portal, etc) via Mobile Publisher, while also embedding Tableau Visualizations in their portal.
Tableau Server has it’s own session authentication cookies and will run embedded through an iframe, so in the eyes of the iOS device of the end-user, those are considered thrid-party cookies and hence, iOS will simply block them. Even though these are genuine third-party auth cookies (and not an advertising one), there’s really nothing you can do in the settings of the Mobile Publisher app to make them work.
Here’s this post to the rescue! We’ve finally managed to win this iOS cookie saga by creating an environment that “fools” iOS to believe all cookies (from Salesforce Experience Cloud and Tableau Server) are coming from the same domain. In other words, all cookies will be first-party cookies and everything will just work as expected.
Together with a few talended Solution Engineers at Tableau and Salesforce, we’ve achieved that by running Tableau Server behind a Reverse Proxy under the address https://twiki.life. Simultaneusly, we’ve used a Custom DNS on our Salesforce Community Portal to run on a subdomain: https://sf.twiki.life (i.e. from an iOS perspective, all cookies come from twiki.life domain :)
(note: you could choose to do the op
Here’s an example of how our solution architecture looks like:
And here’s short video discussing this issue and this solution:
We’ve got the following solution building-blocks implemented more or less in this order:
Part 1: Custom Domain for our Salesforce Tenant and Experience Cloud Community
- A domain created on our registrar with proper CA-signed wildcard SSL certificate for our domain, e.g: *.twiki.life. Note: alternatively you could have a different cert for each subdomain (e.g. one for our Salesforce Custom domain to run under sf.twiki.life; one for Tableau Server to run under twiki.life) .
2. We’ve imported that (sf.twiki.life) CA-signed ssl certificate into Salesforce. High level step-by-step we used here.
2.1. We’ve converted the certificate from step 1 into Java Keystore JKS format, as required by Salesforce. Here are the detailed instructions we used (don’t forget the last step to change the alias from 1 to your own (e.g. sf.twiki.life), otherwise Salesforce will not let you import this JKS).
2.2. Then, we’ve followed this guide to understand how to import it properly (it wasn’t so trivial, for instance we had to enable SF Identity with a self-signed certificate ahead of the actual cert import).
We ended up with something like this:
3. We’ve configured Salesforce Professional Community Portal (Experience Cloud) to run with your custom sub-domain, e.g: sf.twiki.life
(Step 4 needs to be done in conjunction, for step 3 to successfuly accept the domain).
4. Back on your domain registrar, we’ve added a CNAME entry, pointing sf.twiki.life to the alias destination we got from Salesforce (as seen above screenshot), e.g: sf.twiki.life.00d09000007wm7xeau.live.siteforce.com. (note: the point in the end is important, not a typo ;)
We also had an A record pointing to the machine we were running our Reverse Proxy (more on this below on Part 2 of this guide).
Remember: It can take some time for all DNS servers around the world to receive information about this change!
5. In Salesforce, we’ve created a custom URL for our new domain pointing to our Experience Cloud Cummunity page (/gogo). In addition, SF auto-generated this URL (poitning to the the actual Community Home page: /gogo/s) :
Ok, our custom domain is ready. If we point the web browser to https://sf.twiki.life/gogo/s it takes us to the Community Home Page, yey!!
Part 2: The Embedded Tableau Viz part:
6. Focusing on Tableau Server now: We’ve configured all traffic coming from the internet and reaching the server to pass through a Reverse Proxy configured to run on the same domain (twiki.life). So we used ngnix and configured it to serve https traffic on port 443 with the certificate we got for twiki.life domain in step 1 above. And we pointed it to where our Tableau Server was actually hosted (e.g. https://myts.westeurope.cloudapp.azure.com).
In summary: DNS config on Registra (twiki.life) → A record to Nginx server IP address → nginx route to Tableau Server VM running on Azure
Here’s how my nginx.conf file looked like:
To test that our nginx reverse proxy was working as expected, we pointed the browser to https://twiki.life and it worked!
To embed the visualization in the Community Portal, we’ve used the Tableau Lightning App Component (this is pre-installed in every Salesforce org by default).
Alternatively you could use your own LWC or even VisualForce pages with the solution of this blog post, including your own SSO method of choice, we’ve tested all of them, being on the same domain solves all issues!
Part 3: Single Sign-On
Now the last building block in the equation above was a SSO solution, so that users need only to sign in to Salesforce comminity and it they get automatically signed into Tableau. That way, we can even leverage the same dashboards with multiple end users with row-level security.
You could use any SSO method of you choice. The best candidates are SAML and Tableau Connected Apps. We’ve used SAML, because it works flawlesly with SF Identity and Tableau’s LWC Embed Component for Salesforce.
8. SAML SSO: We’ve configured Salesforce Identity as a SAML SSO provider for Tableau Server, following this article: Configure Tableau for Server-wide SAML.
8.1. We’ve first completed step 1 in Tableau Server. Note: the saml certificate files I used: sf.cer (it was an export of the certificate we had in Salesforce for sf.twiki.life (from step 2 above). The cert key (private.key) is the one we got when we generated a certificate from the registrar in step 1.
8.2. Then we downloaded XML Metadata File. Opening this file in a text editor, we found some important values we needed to configure SF Identity SAML idp. We’re looking for the ACS Url(Assertion Consumer Service):
8.3. We went in Salesforce and followed this guide to Enable Salesforce as an Identity Provider and to create a new (SAML) Connected App for Tableau Server
8.4. Here’s the SAML idp (Connected App) config in Salesforce. You will need the highlighted ones at least, coming from Tableau (steps 8.1 and 8.2 above)
8.5. After that, we’ve exported the correct idP metadata, from SF Professional Comminity, as this was the one using our custom domain (sf.twiki.life):
8.6. We’ve uploaded this metadata back in Tableau SAML’s config page (step 4).
Here’s how the settings ended up looking like in Tableau’s TSM interface:
- Notice the Username = username → this is super important: The same email used in Salesforce is configured as username claim for SAML. We need this exact email configured as username in Tableau:
8.7. Enable iframe authentication for SAML (so that when the SF Commuinity user signs in and requests a Tableau Dashboard, the Tableau server session will be created seamlessly from the iframe (remember, no cookies will be blocked, as we’re running everything from the same domain :)
Testing everything together
- And now we can login and navigate to the page that contains the Tableau Dashboard embedded. And it works!!!!!!!!!!
This is beautiful!
Before running Tableau Server and the Community portal under the same domain, the app would never succeed showing the dashboard because iOS webkit engine would be strip out the session cookie. We’ve solved it!!!
That’s it folks. Hope it helps!