Securely Passing Parameters into a Tableau Viz at Load Time

The standard answer for enforcing user-based data entitlements in Tableau is to use Row Level Security, where the user is authenticated in Tableau Server and then tied into an “entitlements view” in the database so that the user only ever sees data they have access rights to.

However, we are very often asked about passing parameters in to the viz to filter down information directly at load time, often driven by an application that Tableau vizes are embedded in. This post is about a few methods of implementing this behavior, and the security implications of each of them.

Basics of Security

Everything must be HTTPS

I’ll start by saying, to do any of this securely, you need EVERY resource you are working with to be using the HTTPS protocol (latest TLS version). If anything is not HTTPS, you could be passing important information in the clear.

Using URL Parameters to set a Filter directly is NOT SECURE

You can use the URL Parameter syntax to directly set the values for a Filter on any field, but this is completely insecure. Why? Because the following two methods will clear any filter and reveal all of the rows of data. Unless you have the JS API turned off, there is no way to prevent this.

Sheet.applyFilterAsync(fieldName, "", tableau.FilterUpdateType.ALL);

Tableau Parameters are the (potentially) secure way to make an adjustable Data Source Filter

The only way to prevent a user from resetting a filter value is by making it a Data Source Filter.  Thankfully, you can use a Calculated Field for the Data Source Filter. If you use Tableau Parameters in the Calculated Field, the Parameter value(s) can be set to change what is filtered, and you will have a Data Source Filter that cannot be altered by the JS API (or the end user).

However, there are quite a few considerations to make this a truly secure method for setting filter values:


Tableau and Write-Back – Together At Last

Editor’s Note: Huge thanks to special contributor Gordon Rose for this blog post.

Tableau helps people see and understand their data – and guarantees that it in the process, it will never make any changes to that data. Tableau is a strictly read-only technology. However, many customers want the ability to modify the data that lies behind a Tableau visualization (Viz), and then, either see those changes immediately reflected in the Viz and/or make other applications aware of those changes. With a small amount of supporting technology, Tableau’s read-only behavior can easily be integrated into so-called “write-back” use cases.

In this blog article, we’ll explore a way to do exactly that – one in which the write-back components are external to the Viz. An alternative approach is one in which those components are more tightly integrated into the Viz itself – that’s for a later blog article to explore. Ideally you will find that you can use one of these two approaches as a launching point for the development of your own write-back use case.


Killing a Tableau Server Session

Within an embedded application, it can be difficult to make sure that sign-out is achieved in both the application and Tableau Server. Tableau Server supports SAML signout commands, but for all sorts of reasons, this might not always work.

Luckily, it is possible to use the REST API to kill any session programmatically, but you need the session identifier from the Tableau Repository. The question is, how do you know what session belongs to a user? There is a sessions view, but you need a little bit more to get filtering down to the username level:

system_users.name AS user_name,
FROM sessions
JOIN users ON sessions.user_id = users.id
JOIN system_users ON users.system_user_id = system_users.id
WHERE system_users.name = '{username}'

Once you have the session ID, you can send a REST API sign out command.

tableau_tools has both of these commands wrapped in a simple interface.

server = ''
username = ''
password = ''
readonly_user_password = ""
d = TableauRestApiConnection25(server, username, password)
tab_rep = TableauRepository(server, readonly_user_password)
uname = 'some_username'
sessions_cur = tab_rep.query_sessions(username=uname)
for row in sessions_cur:

The signout() method has an optional parameter called “session_token”, that accepts the values of the session_id column of the sessions table and _sessions views (when they are set to the old style of ID — see below). Older versions of tableau_tools had you set the token property directly, but the only way to accomplish this now is through the signout(session_token=”session_id”) method.

Using this in later versions of Tableau Server (10.1+)

In Tableau 10.1, there was a change to the structure of Session IDs to provide additional security features. Unfortunately, this broke the technique listed above. If you do have the need for manually killing sessions via the REST API, you’ll need to revert back to the previous type of Session ID using the following tabadmin setting (or its equivalent in TSM in 2018.2+)

tabadmin set features.ProtectVizPortalSessionIds false

tabadmin configure

Yes, you will need to restart the server for that change to take effect.

Using the JavaScript API without Embedding

Tamás Földi always has amazing stuff, but this one really blows my mind- Tableau JavaScript API without Embedding .

Using the Web Data Connector to publish arbitrary extra content to Tableau Server is pretty amazing when you think about it; it also works for embedding web edit in an iframe.

Update for more recent Tableau Server versions (2017-11-15):

We recently encountered a customer wanting to implement this functionality and they were having trouble accessing the getWorkbook() method of the Viz object. Apparently Tableau Server now makes the Viz object available prior to it being fully initialized, and any web content starts to load immediately. So if you want to use this technique, you should put in an interval check to keep trying until the workbook is actually available. It looks something like this:

var myInterval;
function start_timer(){
myInterval = setInterval(js_api_test, 200); //https://www.w3schools.com/jsref/met_win_setinterval.asp
function js_api_test() {
try {
var sheet = parent.parent.tableau.VizManager.getVizs()[0].getWorkbook().getActiveSheet(); //Error: "Unable to get property getWorkbook()"
clearInterval(myInterval); //https://www.w3schools.com/jsref/met_win_clearinterval.asp
console.log("Successfully retrieved Tableau sheet object");

// start doing anything else you'd like to do here

catch (err) {
console.log("No dice!");

You would place a call to the start_timer() function in the onload function of the body tag of your HTML page. Rather than jump directly into the code, you start a repeating interval timer that puts in a bit of delay, and will retry until it actually gets the workbook object. Once that object is found, the interval is cleared and you can start doing whatever you want relative to the viz the page is embedded within.


Using Trusted Tickets for SSO Login to the Tableau Server UI (VizPortal)

There are many situations where a customer wants to have an SSO integration with Tableau Server, but wants the end user to have full access to the Tableau Server UI (called VizPortal), as opposed to embedding Tableau views directly into the product (which only invokes VizSQLServer). This is not an issue when using SAML authentication, but Trusted Authentication requires that the trusted ticket be redeemed against a valid workbook.

By using the fact that the redemption of a Trusted Ticket creates a Tableau Server Session in the browser, the following technique will allow a smooth SSO process directly into the Tableau Server UI.


Scheduled e-mails from embedded Tableau visualizations

UPDATE 2019-04-09: The previous versions of this blog post suggested ending the URL you input into the Tableau Settings box with with a “?=” to allow your application to listen to the Tableau view URL as a GET parameter. This does not work in more recent versions of Tableau Server. Tableau Server appears to separate out the “?=” and put it at the end no matter what.

Big thanks to Reed Walton at Tableau for coming up with the basics of this solution off the top of his head so many years ago.

Scheduling e-mails is easy in Tableau Server — as long as an SMTP server has been specified in the “Configure Tableau Server” program, there will be an e-mail subscription icon in the top of a viz when the user has permissions. The e-mail that is sent includes a PNG snapshot of the viz with a link to go directly to the view in Tableau Server. This poses a problem if you are embedding the views in your own portal and only want users to experience the embedded experience, as they will hit the Tableau Server vizportal UI directly, without the ?embed=y parameter and without any single sign-on.

Setting up Tableau Server for e-mails to go to a “redirect location”

Whatever you put in the “Tableau Server URL” box of the Notifications->Email Server page will have one of the following four patterns appended to it (the first two for Default Site, and the second if a non-default Site)

  • /views/{WorkbookContentUrl}/{ViewContentUrl}
  • /views/{WorkbookContentUrl}/{ViewContentUrl}/{username}/{CustomViewUrl}
  • /t/{siteContentUrl}/views/{WorkbookContentUrl}/{ViewContentUrl}
  • /t/{siteContentUrl}/views/{WorkbookContentUrl}/{ViewContentUrl}/{username}/{CustomViewUrl}

When the e-mail is sent, it will combine your “Tableau Server URL”  setting with these endings. The trick here is that instead of putting in the actual URL of your Tableau Server, instead you put in a URL that goes to your web application. It doesn’t have to be a plain fully-qualified domain — you can make a complex pattern.

For example, if we entered


in the “Tableau Server URL” box, then an e-mail scheduled from the “emea” Site on my Tableau Server on the Workbook/View with contentURL: “EUMigrationCrisis/Wherethemigrantstravel” would end up like the following in the e-mail:


Redirecting from the Link

This is where your own application environment will determine how you actually implement the solution, however there are multiple ways which all should work.

The process involves:

  1. When a URL that follows the pattern from above comes in (specifically the portion from the “Tableau Server URL”), recognize it for further processing
  2. Grab everything after the “Tableau Server URL” portion, and pass it to the page in your Web Application that displays Tableau Content
    •  You can split this portion up into its components, but it should match exactly to what is needed to load a view using the Tableau JavaScript API

How do you actually do this?

In an application like Node.js (JavaScript) or Django (Python), you may already be defining URL Routes which can translate URL components into arguments to send to a view function / method (Django example with some missing angle braces):

path('email/t/str:site/views/str:workbook_content_url/str:view_content_url/', views.tableau_content, name='tableau-content-email'),
path('email/t/str:site/views/str:workbook_content_url/str:view_content_url/str:username/str:custom_view_url/', views.tableau_content, name='tableau-content-email-custom-view'),

You might instead process the URLs at the web server level right when the come in and rewrite them as GET requests to a given active page:


translated to:


These are just examples, but hopefully with the principles defined, you’ll be able to accomplish this in the environment your existing web application exists in without too much trouble.

Utilizing Tableau Server’s Search Server in an Embedded Portal

Tableau Server 9.0 has an amazing search functionality — if you type in values into the top search box, it looks across everything available in the Tableau Server instantly and brings back results incredibly quickly. It’s not any particular secret that the Search & Browse process is powered by Apache Solr/Lucene . It’s a blazing fast piece of technology that supports a lot of the instantaneous feel in Tableau Server 9.0 (the portal and the REST API also use Solr).

I was asked recently how to do some of the same search functionality that exists in Tableau Server 9.0, but in an embedded portal. Some of it is possible via the REST API, while other requests would require opening up the PostgreSQL Repository. I wasn’t even sure some of the requests were possible — yet when I typed into the multi-search box, it seemed to be searching across all of the attributes we were looking to tap into.