接続済みアプリによる埋め込みビューの SSO を有効化してみた #tableau

2024.03.21

はじめに

Tableau Server でパブリッシュしたビューは、他の Web サイトに埋め込むことが可能です。
通常、そのビューを閲覧するためには Tableau Server へのログイン情報の入力が必要ですが「接続済みアプリ」という機能を使用すると、ユーザーには認証情報を求めずにビューが表示され、ユーザーエクスペリエンスを向上できます。
この機能を試してみたのでその手順を記事としました。

前提条件

  • Tableau Server
    • バージョン:2023.3.3
    • OS:Windows Server 2019
    • 構成
      • 単一サーバー構成
      • アイデンティティストア:ローカル
      • 前段に Application Load Balancer(ALB)のある構成
    • Route53 にて検証用のDNS ホストゾーンを作成済み
      • クライアントからは、CNAME レコードを使用して Tableau Server にアクセス
    • ALB で SSL オフロードを行う
      • Tableau Server の外部 SSL は未実施
      • ACM で取得した証明書を ALB リスナに関連付け
      • リバース プロキシ サーバーを使用するための Tableau Server の構成を実施済み
  • 簡易 Web アプリケーションサーバー
    • OS:Amazon Linux2
    • インスタンスタイプ:t2.micro
    • Tableau Server には HTTPS でアクセス可能

上記構成での Tableau Server のインストール手順については、以下の記事をご参照ください。

接続済みアプリ

Tabeau Server のビューを閲覧する際は認証が必要なため、ビューが外部の Web アプリケーションに埋め込まれており、Web アプリケーション側にユーザーを認証する手段が既にある場合、ユーザーはサインインを2度行う必要があります。
Webアプリケーションにサインインしたユーザーが Tableau Server にもサインインする必要がないようにするには、シングルサインオン(SSO)を有効にする必要があります。
これまで Tableau への SSO を有効にするためには、「信頼できる認証」や Tableau Server を SAML または OpenID を使用するように構成する必要があり、SAML や OpenID では、外部 IdP が必要でした。

Tableau Server バージョン 2022.1 以降からは「接続済みアプリ」という機能が提供され、これにより、アプリケーションと Tableau Server 間は、JSON Web Token(JWT)標準の認証トークンによって明示的な信頼関係を構築できるようになります。

Tableau Server:ビューの作成

前提条件の手順で構築した Tableau Server 上で下図のようなダッシュボードを作成しました。このダッシュボードを Web ページに埋め込みます。

Tableau Server で作成したビューを表示し [共有] から [埋め込みコードをコピー] をクリックします。

すると、以下のような形式の埋め込みコードをコピーできます。このコードを Web ページに埋め込むことで、ビューを表示することができます。

<script 
	type='module' src='https://<Tableau ServerのFQDN>/javascripts/api/tableau.embedding.3.latest.min.js'>
</script>
<tableau-viz
 id='tableau-viz' src='https://<Tableau ServerのFQDN>/views/iris-dashboard/1' width='1000' height='840' hide-tabs toolbar='bottom' >
</tableau-viz>

埋め込みコードを取得した段階では、latest.minとして埋め込み API のバージョンが指定されます。
明示的にバージョンを指定する場合、対応するバージョンを以下から確認できます。
Access Tableau Embedding API v3

簡易な Web アプリケーションの用意

ここでは Python で利用でき Micro Web Framework として動く Flask を EC2 上で使用します。

簡易 Web アプリケーションサーバー用の EC2 にSSH 接続し、以下の通り Flask を使用できるように準備します。
Python のインストール確認

$ sudo yum update 
$ python3 --version
Python 3.7.16

pip のインストール確認

$ pip3 --version
pip 20.2.2 from /usr/lib/python3.7/site-packages/pip (python 3.7)

Flask のインストール

sudo pip3 install flask

Flask アプリのディレクトリを作成

mkdir flask
cd flask
vi app.py

app.py の中身として、はじめは、以下の通り通常の埋め込みを行いました。

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return '''
    <!DOCTYPE html>
    <html>
    <head>
        <title>Tableau Embedding Example - Iris Dataset Visualization</title>
        <style>
            body {
                text-align: center;
                margin: 0;
                padding: 0;
                height: 100vh;
                display: flex;
                flex-direction: column;
                justify-content: center;
            }
            #tableau-viz {
                margin: 0 auto;
            }
        </style>
    </head>
    <body>
    <h1>Iris Dataset Visualization</h1>
    <p>Below is an embedded Tableau visualization of the Iris dataset.</p>
    <script	type='module' src='https://<Tableau ServerのFQDN>/javascripts/api/tableau.embedding.3.latest.min.js'>
    </script>
    <tableau-viz id='tableau-viz' src='https://<Tableau ServerのFQDN>/views/iris-dashboard/1' width='1000' height='840' hide-tabs toolbar='bottom' >
    </tableau-viz>    
    <p>This visualization showcases the distribution of iris flowers across various dimensions.</p>
    </body>
    </html>
    '''

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)

ファイルを作成後、以下のコマンドでアプリを起動します。

flask run --host=0.0.0.0 --port=8080

手元のクライアントからアクセスすると、下図の表示になります。

上述の通り、ビューを表示するには Tableau Server へのサインインが必要なので、サインインすると、下図の表示となります。

サインインしたとしても、ブラウザの Cookie を削除することで、再度ログイン画面が表示されるようになります。
※下図は Chrome の例

Tableau Server:接続済みアプリの構成

Tableau Server にログインし「設定 > 連携アプリ」を開きます。

[新しい連携アプリ] をクリックすると下図のポップアップが表示されます。

各項目は以下の通りです。

  • 連携アプリの名称
    • Tableau Server 側で連携アプリを識別する任意の名称を指定します
  • アクセスレベル
    • 接続済みアプリのアクセスレベルとして、以下のいずれかを選択できます。これにより、対象の接続済みアプリでどのコンテンツを埋め込むことができるかを制御します
      • すべてのプロジェクト:すべてのプロジェクトのコンテンツを埋め込むことが可能となります
      • 1 つのプロジェクトのみ:指定されたプロジェクト内のコンテンツのみを埋め込み可能となります
  • ドメイン許可リスト
    • Tableau のビューを埋め込み可能な Web アプリケーションのドメインを制限できます

ここでは適用先を「すべてのプロジェクト」、ドメイン許可リストを「すべてのドメイン」として作成しました。

接続済みアプリを作成すると、下図の表示となります。

[シークレットの新規生成] をクリックするとシークレット(ID・値)が生成されます。

ここで確認できる以下の情報は、後にアプリケーション側で必要となります。

  • シークレット ID
  • シークレットの値
  • クライアント ID

既定では接続済みアプリのステータスは「無効」なので、使用する際はこちらを有効化します。

アプリの変更

アプリケーション側に SSH 接続し、フォルダ構成を以下とし、テンプレートとなる index.html を作成します。

flask/
├── app.py
└── templates/
    └── index.html

index.html

<!DOCTYPE html>
<html>
<head>
    <title>Tableau Embedding Example</title>
    <style>
        body {
            text-align: center;
            margin: 0;
            padding: 0;
            height: 100vh;
            display: flex;
            flex-direction: column;
            justify-content: center;
            font-family: Arial, sans-serif;
        }
        #tableau-viz-container {
            margin: 20px auto;
        }
        p {
            margin: 20px 0;
        }
    </style>
</head>
<body>
    <h1>Iris Dataset Visualization</h1>
    <script	type='module' src='https://<Tableau ServerのFQDN>/javascripts/api/tableau.embedding.3.latest.min.js'>
    </script>
    <p>Below is an embedded Tableau visualization of the Iris dataset.</p>
    <div id='tableau-viz-container'>
		    <tableau-viz id='tableau-viz' src='https://<Tableau ServerのFQDN>/views/iris-dashboard/1' 
          width='1000' height='840' hide-tabs toolbar='bottom' token='{{ token }}'>
        </tableau-viz>
    </div>
    <p>This visualization showcases the distribution of iris flowers across various dimensions.</p>
    <script>
        // トークンを取得してTableau Viz のトークンを更新
        var tableauViz = document.getElementById("tableau-viz");
        tableauViz.setAttribute("token", "{{ token }}");
    </script>
</body>
</html>

<tableau-viz> 要素内のtoken='{{ token }}' に、Tableau Server にアクセスするためのトークンが動的に挿入されます。
埋め込み API については以下のドキュメントをご参照ください。

app.py の変更

ここでは、接続済みアプリで取得したシークレットの情報を環境変数から取得することとし、app.py を変更します。

export CONNECTED_APP_CLIENT_ID="クライアント ID"
export CONNECTED_APP_SECRET_KEY="シークレットの値"
export CONNECTED_APP_SECRET_ID="シークレット ID"
export USER_NAME="Tableau Server のユーザー名"

app.py

from flask import Flask, render_template
import jwt
import datetime
import uuid
import os

app = Flask(__name__)

# トークン生成
def generate_token():
    # 環境変数から値を取得
    connectedAppClientId = os.getenv("CONNECTED_APP_CLIENT_ID")
    connectedAppSecretKey = os.getenv("CONNECTED_APP_SECRET_KEY")
    connectedAppSecretId = os.getenv("CONNECTED_APP_SECRET_ID")
    user = os.getenv("USER_NAME")

    token = jwt.encode(
        {
            "iss": connectedAppClientId,
            "exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=5),
            "jti": str(uuid.uuid4()),
            "aud": "tableau",
            "sub": user,
            "scp": ["tableau:views:embed"]
        },
        connectedAppSecretKey,
        algorithm="HS256",
        headers={
            'kid': connectedAppSecretId,
            'iss': connectedAppClientId
        }
    )
    return token

# ルートパスにアクセスした際の処理
@app.route('/')
def index():
    # トークンを生成
    token = generate_token()

    # HTML テンプレートにトークンを挿入して返す
    return render_template('index.html', token=token)

if __name__ == '__main__':
    app.run(debug=True)

上記のコードでは、シークレットの情報を使用したトークンを生成し、それを含む HTML ページを作成しています。トークンの生成については、以下にサンプルコードの記載があるのでこちらを参考にしています。

また、ここでは決まったユーザー名を使用していますが、実際には Web アプリケーションからビューを表示するユーザーごとにユーザー名を指定します。

アプリケーションの起動

上記の設定でアプリケーションを起動し、Cookie が削除済みの状態で、クライアントからアクセスすると、先のような認証画面が表示されず、すぐにビューが表示されました。Cookie を削除しても、同様にログイン画面は表示されません。

再度、app.py を変更し、変更前のトークンを使用しない状態に戻すと、サインイン画面が表示されました。

ドメイン許可リストの指定

上記の状態から、接続済みアプリのドメイン許可リストを適当な値に設定してみます。

更新後、ブラウザを更新するとビューが表示されなくなりました。

ユーザーにビューの閲覧権限がない場合

この場合もドメイン許可リストの時と同様にビューが表示されなくなりました。(権限がない、などのメッセージはブラウザ上には表示されませんでした。)

さいごに

記事では Tableau Server で試してみましたが、Tableau Cloud でも同様の手順で SSO 設定が可能です。
こちらの内容が何かの参考になれば幸いです。

参考