Skip to content

Commit 70a5cea

Browse files
authored
Merge pull request #374 from tableau/dev
Doc update for OAuth and Tableau 2021.1 changes
2 parents f57e822 + 59be0b1 commit 70a5cea

File tree

5 files changed

+216
-6
lines changed

5 files changed

+216
-6
lines changed

_includes/docs_menu.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@
5757
<li>
5858
<a href="{{ site.baseurl }}/docs/trex_sandbox_test.html">Create and Test Sandboxed Extensions</a>
5959
</li>
60+
<li>
61+
<a href="{{ site.baseurl }}/docs/trex_oauth.html">Add OAuth to Dashboard Extensions</a>
62+
</li>
6063
<li class="nav-header">Debugging and Troubleshooting</li>
6164
<li>
6265
<a href="{{ site.baseurl }}/docs/trex_debugging.html">Debug Extensions in Tableau Desktop</a>

docs/trex_debugging.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ Tableau Desktop version | Chromium version | Chrome version
2626
|----|----|----|
2727
2018.2, 2018.3 | 47.0.2526.0 | Not available
2828
2019.1 and later | 79.0.3945.0 | Chrome version 79 or earlier.
29+
Latest maintenance release of 2020.2.7+, 2020.3.6+, 2020.4.2+ | 87.0.4280 | Chrome version 80 or later.
30+
2021.1 and later | 87.0.4280 | Chrome version 80 or later.
31+
2932

3033

3134
**Chromium downloads for debugging Tableau 2018.2, 2018.3**
@@ -41,9 +44,7 @@ Tableau Desktop version | Chromium version | Chrome version
4144

4245
* [Chromium for macOS (`chrome-mac.zip`) (79.0.3945.0)](https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Mac/706915/)
4346

44-
45-
<div class="alert alert-info"><b>Note </b> If you are using Tableau 2019.1 or later, you can debug extensions in Tableau Desktop using certain versions of Chrome (versions prior to 80). Currently, you can't use Chrome version 80 (or later) for debugging your extension.</div>
46-
47+
<div class="alert alert-info"><b>Note </b> If you are using Tableau 2021.1, or the latest maintenance releases of Tableau 2020.2.2.7+, 2020.3.3.6+, and 2020.4.2+, you can use Chrome version 80 (or later) for debugging your extension.</div>
4748

4849
---
4950

docs/trex_known_issues.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ layout: docs
55

66
The following section describes some issues in the current release of the Extensions API where the API or the platform does not behave as expected.
77

8-
For information about what is new or has changed in each release, see the [Release Notes for the Tableau Extensions API]({{ site.baseurl }}/docs/trex_release-notes.html)
8+
For information about what is new or has changed in each release, see the [Release Notes for the Tableau Extensions API]({{site.baseurl}}/docs/trex_release-notes.html).
99

1010
**In this section**
1111

@@ -17,6 +17,22 @@ For information about what is new or has changed in each release, see the [Relea
1717
Because of incompatibilities between Chrome and the internal Chromium-based browser used in Tableau, you can't use Chrome version 80 or later to debug your extensions. If you are using Tableau Desktop 2019.1 or later, you can debug extensions using Chrome version 79 or Chromium version 79. If you are using Tableau Desktop versions 2018.2 or 2018.3, you can use Chromium version 47. For more information about debugging extensions and using the Chromium browser, see [Debug Extensions in Tableau Desktop]({{site.baseurl}}/docs/trex_debugging.html) and [Download the Chromium Browser]({{site.baseurl}}/docs/trex_debugging.html#download-the-chromium-browser).
1818

1919

20+
### Unable to run dashboard extension using self-signed certificates
21+
22+
Tableau now uses Qt WebEngine 5.15, which is based on Chromium version 87.0.4280, with additional security fixes from newer versions of Chromium. Because of this update, dashboard extensions hosted on web servers that use self-signed certificates (SSL) might not work in Tableau 2021.1, or in the most recent Tableau maintenance releases: 2020.2.7+, 2020.3.6+, and 2020.4.2+.
23+
You might see one of the following errors:
24+
25+
* `Failed to load resource: net::ERR_CERT_COMMON_NAME_INVALID`
26+
27+
* `Error: Subject Alternative Name Missing`
28+
29+
* `Your connection is not private`
30+
31+
You can avoid these errors if you specify the `subjectAlternativeName` (SAN) in the extended certificate parameters when you sign your certificate.
32+
33+
For more information, see [Google Chromium Enterprise Known Issues - Error "Subject Alternative Name Missing"](https://support.google.com/chrome/a/answer/9813310?hl=en#zippy=%2Cerror-subject-alternative-name-missing-or-neterr-cert-common-name-invalid-or-your-connection-is-not-private){:target="_blank"} and the following discussion on Stack Overflow: [Invalid self signed SSL cert - “Subject Alternative Name Missing” on StackOverflow](https://stackoverflow.com/questions/43665243/invalid-self-signed-ssl-cert-subject-alternative-name-missing){:target="_blank"}.
34+
35+
2036
### Time zone not persisted when updating date parameter
2137

2238
When you update a date or date-time parameter using `changeValueAsync()`, the time zone information is not kept. The date/time is still correct, however, it is just that the data/time is converted to UTC.
@@ -39,7 +55,7 @@ If your extension uses the `getDataSourcesAsync()` method, calling this method m
3955

4056
### Full data access and permission errors
4157

42-
When an extension needs full data access and the user does not have full data permission on the workbook, Tableau currently allows the extension to run. However, Tableau will throw a console error when the extension calls `getUnderlyingData()` method. See [Handle full data access and permission errors]({{ site.baseurl }}/docs/trex_getdata.html#handle-full-data-access-and-permission-errors).
58+
When an extension needs full data access and the user does not have full data permission on the workbook, Tableau currently allows the extension to run. However, Tableau will throw a console error when the extension calls `getUnderlyingData()` method. See [Handle full data access and permission errors]({{site.baseurl}}/docs/trex_getdata.html#handle-full-data-access-and-permission-errors).
4359

4460

4561
### Tableau Extensions API library version 1.0.0

docs/trex_oauth.md

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
---
2+
title: Add OAuth to Dashboard Extensions
3+
layout: docs
4+
---
5+
6+
If you want your dashboard extension to use OAuth for authentication, you need to be aware of some important changes that were introduced along with sandboxed extensions in Tableau 2019.4. All dashboard extensions are now wrapped in an `iframe`. Because most OAuth sign-in pages enforce the `X-Frame-Options` settings, which helps prevent [clickjacking](https://tools.ietf.org/html/draft-ietf-oauth-v2-23#section-10.13) attacks, you can't load an OAuth sign-in page within the dashboard, or open a dashboard extension dialog window to use for OAuth sign in.
7+
8+
As a best practice, dashboard extensions should open an external browser window when you need to request end-user authorization. In Tableau Server and Tableau Online, you can do this by using methods like `window.open()` and `window.postMessage()`. However, the `window` methods alone will not work in Tableau Desktop, as the external browser window you open for OAuth sign in has no direct connection to the instance of the dashboard extension. This adds a layer of security and requires extra steps to properly route the access token back to your dashboard extension. The following section describes one possible way of coordinating the OAuth interaction using web sockets for communication and the authorization code grant. For security, the client secret should be stored on the server and not in the client code. This is the only safe way to ensure that someone can't use the client credentials to impersonate your application. To simplify your code, your extension should use the same OAuth flow for Tableau Server and Tableau Desktop.
9+
10+
11+
---
12+
**In this section**
13+
14+
* TOC
15+
{:toc}
16+
17+
---
18+
19+
## Summary of the OAuth flow
20+
21+
The following outlines some of the basic steps in using a secondary window for OAuth sign in. For more guidance on setting up OAuth 2.0 securely, see [OAuth 2.0 Security Best Current Practice](https://tools.ietf.org/html/draft-ietf-oauth-security-topics). The following summarizes the steps you'd take if you were using the authorization code grant type. This OAuth flow is recommended as it ensures that the access token is not returned in the URL and that the client secret is safely stored on the server.
22+
23+
* Open a channel for communication between the server hosting the extension (the server) and the instance of the extension (the client). Create a unique identifier (or session id) for the extension when the extension loads.
24+
25+
* Create a request (URL) that directs the user to the OAuth provider sign-in page. The request URL includes the client ID and the extension's unique identifier (session id), and provides a callback URL. Open a new dialog window using `window.open()` directed to the URL. Prompt the user to sign in to the OAuth provider (the provider).
26+
27+
* The user signs in to the OAuth provider and allows (or denies) the extension access to data.
28+
29+
* When authorization has been granted, the OAuth authorization code is returned to the callback URL (on the server). The server processes the response and extracts the authorization code and the session id. The server creates a new request for the OAuth access token, using the authorization code grant type, and maps the client ID, client secret, and authorization code in the body of the request.
30+
31+
* When the response from the OAuth provider is received, the server signals an event that returns the OAuth access token, through the web socket, back to the calling extension.
32+
33+
* In response to the signal, the extension retrieves and stores the OAuth token in local storage on the client's computer. The extension can then use the access token to send subsequent requests for services from the OAuth provider.
34+
35+
---
36+
37+
## An example of the OAuth code path using Socket.IO
38+
39+
One way to manage OAuth sign in for your dashboard extension is to employee web sockets for identifying and coordinating the OAuth connection. For example, the OAuth sample ([datadev-oauth-sign-in](https://glitch.com/~datadev-oauth-sign-in){:target="_blank"}) on Glitch makes use of the [Socket.IO](https://socket.io){:target="_blank"} library. Socket.IO provides bi-directional event-based communication between the browser (client) and server. This example uses Node.js and Express to setup a web server to host an extension that connects to Spotify. The extension uses sockets to identify the extension and to route communication with the OAuth sign-in window.
40+
41+
42+
### Initialize the socket (session ID)
43+
44+
In the OAuth sample, when the extension loads in the dashboard, a unique socket session ID is generated. The socket instance is assigned a random 20-character identifier (`socket.id`). This code is on the client-side, the extension's web page (`index.js`). The code also initializes the sign-in button on the extension's home page.
45+
46+
```javascript
47+
48+
const socket = io();
49+
50+
// Wait to make sure you've made the socket connection so when you press the button there is a socket ID available to send with the request
51+
socket.on("connect", () => {
52+
$("#submit").prop("disabled", false);
53+
});
54+
55+
56+
```
57+
58+
On the Express server (`server.js`), in addition to serving the web pages, the server initializes `socket.io`.
59+
60+
```javascript
61+
62+
const express = require("express");
63+
const app = express();
64+
const server = require("http").createServer(app);
65+
const io = require("socket.io")(server);
66+
const fetch = require("node-fetch");
67+
68+
69+
```
70+
71+
72+
73+
### Sign in and get the authorization code
74+
75+
When a user clicks the sign-in button, it opens a new window or tab in the user's native browser (`window.open`) passing with it the client ID and the socket session ID (`socket.id`). In this example, the URL goes to the authorization endpoint on Spotify and specifies the authorization code response type. Note that this sample uses the client ID for the OAuth sample project on Glitch. You need to change this to match your client ID for your dashboard extension. The method also provides a redirect URL back to the server, where the response will return the authorization code.
76+
77+
```javascript
78+
79+
// Open a new window to manage the OAuth process outside of Desktop, passing the socket ID so the broker knows where to return the token
80+
function openSignInWindow() {
81+
const scopes = "user-top-read";
82+
const redirect_uri = "https://datadev-oauth-sign-in.glitch.me/complete";
83+
const url =
84+
"https://accounts.spotify.com/authorize?response_type=code&client_id=" +
85+
"2029812009c54aef866bd660f612b603" +
86+
"&scope=" +
87+
encodeURIComponent(scopes) +
88+
"&redirect_uri=" +
89+
encodeURIComponent(redirect_uri) +
90+
"&state=" +
91+
socket.id;
92+
window.open(url, "_blank");
93+
}
94+
95+
96+
```
97+
98+
### Request the access token (server-side)
99+
100+
The redirect URL sends the response to the server, where the server extracts the authorization code. The server then sends a new request for the OAuth access token, providing the client ID, the client secret, and authorization code. Making this request from the server ensures that the client secret is kept protected and out of the client-side browser code. In this example, the request is made as part of an `async` function call and the sample makes use of the node-fetch module. There is a lot going on here. After sending the request, the server waits for the response from Spotify that returns the OAuth access token. When the access token is received, the server emits a `signedin` event that signals the instance of extension that initiated the sign in. The server redirects the client browser to the complete page and sends the client the access token, so that the extension can make the authorized requests from Spotify.
101+
102+
```javascript
103+
app.get("/complete", async (req, res) => {
104+
const code = req.query.code;
105+
const sessionid = req.query.state;
106+
107+
const url = "https://accounts.spotify.com/api/token";
108+
const parameters = {
109+
grant_type: "authorization_code",
110+
client_id: process.env.SPOTIFY_CLIENT_ID,
111+
client_secret: process.env.SPOTIFY_CLIENT_SECRET,
112+
code,
113+
redirect_uri: "https://datadev-oauth-sign-in.glitch.me/complete"
114+
};
115+
const body = Object.keys(parameters)
116+
.map(key => `${key}=${encodeURIComponent(parameters[key])}`)
117+
.join("&");
118+
const options = {
119+
method: "POST",
120+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
121+
body
122+
};
123+
124+
const response = await fetch(url, options);
125+
const data = await response.json();
126+
127+
io.to(sessionid).emit("signedin", data.access_token);
128+
res.sendFile(__dirname + "/views/complete.html");
129+
});
130+
131+
132+
133+
```
134+
135+
### Pass the access token to the client and make requests
136+
137+
In response to the `signedin` event, the client checks to see if the OAuth access token was received. If the token is present, the client stores it locally and then uses the access token to make a new request to retrieve the user's top artist from Spotify. In case of error, the access token is removed from local storage and the user is asked to sign in again.
138+
139+
```javascript
140+
141+
socket.on("signedin", function(token) {
142+
if (token) {
143+
localStorage.setItem("spotifyAuth", token);
144+
getTopArtist(token);
145+
} else {
146+
console.error("Token response was empty!");
147+
}
148+
});
149+
150+
// Get the top artist information with the token, if the request fails, prompt again for auth
151+
async function getTopArtist(token) {
152+
const url = "https://api.spotify.com/v1/me/top/artists";
153+
const options = {
154+
headers: {
155+
Authorization: "Bearer " + token
156+
}
157+
};
158+
159+
try {
160+
const response = await fetch(url, options);
161+
const data = await response.json();
162+
updateTopArtist(data);
163+
} catch (error) {
164+
localStorage.removeItem("spotifyAuth");
165+
openSignInWindow();
166+
console.error(error);
167+
}
168+
}
169+
170+
// Updated the text in the dashboard extension
171+
function updateTopArtist(data) {
172+
$("#signIn").hide();
173+
$("#name").html("<b>" + data.items[0].name + "</b> is your favorite artist!");
174+
}
175+
176+
```
177+
178+
### Next steps
179+
180+
* Use the sample code ([datadev-oauth-sign-in](https://glitch.com/~datadev-oauth-sign-in){:target="_blank"}) on Glitch as a starting point to build your own OAuth solution.
181+
182+
* Review the latest [OAuth 2.0 Security Best Current Practice](https://tools.ietf.org/html/draft-ietf-oauth-security-topics) draft documentation.

docs/trex_release-notes.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,18 @@ See also: [Known Issues]({{site.baseurl}}/docs/trex_known_issues.html)
1313

1414
----
1515

16+
### Tableau 2021.1 Updates
17+
*March 2021*
18+
19+
* You can now use Chrome version 80 and later to debug your dashboard extension in Tableau Desktop. If you are using Tableau 2021.1, or the latest maintenance releases of Tableau 2020.2, 2020.3, or 2020.4, you no longer need to use Chromium (version 79 or earlier) for debugging. For more information, see [Debug Extensions in Tableau Desktop]({{site.baseurl}}/docs/trex_debugging.html) and [Download the Chromium Browser]({{site.baseurl}}/docs/trex_debugging.html#download-the-chromium-browser).
20+
21+
* If you plan to implement OAuth in your dashboard extension, you'll want to check out [Add OAuth to Dashboard Extensions]({{site.baseurl}}/docs/trex_oauth.html), and the OAuth sample ([datadev-oauth-sign-in](https://glitch.com/~datadev-oauth-sign-in){:target="_blank"}) on Glitch.
22+
23+
* Because of browser changes in Tableau, dashboard extensions running with self-signed certificates (SSL) might not work in Tableau 2021.1, or in the most recent Tableau maintenance releases: 2020.2.7+, 2020.3.6+, and 2020.4.2+. For more information, see [Known Issues]({{site.baseurl}}/docs/trex_known_issues.html#unable-to-run-dashboard-extension-on-localhost-or-use-self-signed-certificates).
24+
1625
### Tableau Dashboard Extensions API version 1.4
1726
*May 2020*
1827

19-
2028
* Tableau Dashboard Extensions API library: `tableau.extensions.1.4.0.js` <br>(download or clone the Extensions API repository on [GitHub](https://github.com/tableau/extensions-api){:target="_blank"}). <br/>
2129

2230
About this release:

0 commit comments

Comments
 (0)