Skip to main content

Enabling OAuth (Tableau Server)

info

These docs are for enabling OAuth for Tableau Server only.

Tableau Cloud customers should refer to OAuth: Tableau Cloud.

A self-hosted MCP server can instead use your own identity provider with Tableau Server or Tableau Cloud, signing in with Direct Trust.

How to Enable OAuth​

warning

Tableau Server must be version 2025.3 or newer.

To enable OAuth, set the OAUTH_ISSUER environment variable to the origin of your MCP server. When a URL for OAUTH_ISSUER is provided, the MCP server will act as an OAuth 2.1 resource server, capable of accepting and responding to protected resource requests using encrypted access tokens.

When OAuth is enabled:

  • MCP clients will be required to authenticate via Tableau OAuth before connecting to the MCP server
  • The TRANSPORT will default to http (required for OAuth)
  • The AUTH method will default to oauth

For more information, please see the MCP Authorization spec.


Use your own identity provider (self-hosted only)​

A self-hosted MCP server can accept access tokens from your own OAuth or OpenID Connect identity provider (IdP) instead of Tableau. The MCP server checks each token, reads the Tableau username from one claim, and signs in to Tableau as that user with Direct Trust. This mode is not available for hosted Tableau MCP. It is an interim option for self-hosted deployments and may change or be removed in a later release.

To set it up:

  • Set OAUTH_ISSUER to your IdP's issuer and OAUTH_EMBEDDED_AUTHZ_SERVER to false. Tokens with any other iss are rejected.
  • Set OAUTH_EXTERNAL_DISCOVERY to true (JWTs: access tokens or OpenID Connect ID tokens, with the keys read from your IdP's OpenID Connect discovery document).
  • Set AUTH to direct-trust with {OAUTH_USERNAME} in JWT_SUB_CLAIM.
  • Have your IdP set the token's aud to this server's resource identifier: the OAUTH_RESOURCE_URI origin with the /tableau-mcp path, or a value in OAUTH_GLOBAL_RESOURCE_URIS. Clients request it with the resource parameter (RFC 8707). The MCP server accepts any token with a matching aud, so use a value unique to this server. A client ID is fine only if it is this MCP server's own client ID and no other app receives tokens for it. Never use a client ID shared with another app, or a shared default audience such as Okta's api://default.
  • Grant the tableau:mcp:* scopes your tools need in the scope or scp claim. The scopes also keep out tokens without tableau:mcp:* scopes, including ID tokens and tokens meant for other apps. Accepting ID tokens requires OAUTH_DISABLE_SCOPES set to true. The audience is then the only check that ties a token to this server, so do this only when every accepted audience is unique to this server.
warning

Pick a username claim that never changes and that users can't edit, such as sub (the default). Avoid email or preferred_username unless your IdP guarantees them: whoever controls the claim value signs in to Tableau as that user.

Also note:

  • Passthrough auth (ENABLE_PASSTHROUGH_AUTH) runs first. A request with a valid Tableau session is accepted without checking a bearer token.
  • The token's client_id (or azp) claim is used as the client ID, which is sent to telemetry.
  • Each rejected token writes one external-token-rejected record to the audit logger, whatever LOG_LEVEL is set to.

Accept OpenID Connect ID tokens​

If your IdP signs users in with OpenID Connect ID tokens instead of access tokens, the MCP server can accept those as the bearer token. The MCP server reads the keys from your IdP's discovery document:

OAUTH_ISSUER=https://idp.example.com
OAUTH_EMBEDDED_AUTHZ_SERVER=false
OAUTH_EXTERNAL_DISCOVERY=true
OAUTH_GLOBAL_RESOURCE_URIS=<this-mcp-server-client-id>
OAUTH_DISABLE_SCOPES=true
OAUTH_EXTERNAL_USERNAME_CLAIM_NAME=employee_id

Use this MCP server's own client ID only if no other app receives tokens for it: with scopes off, the audience is the only check that ties a token to this server.

On each request, the MCP server checks that:

  • The token is signed with an asymmetric algorithm by a key from the discovery document's jwks_uri.
  • iss equals OAUTH_ISSUER exactly and aud is an accepted value.
  • The token has not expired (exp) and is already valid (nbf, if present), allowing 5 seconds of clock skew.
  • iss, sub, aud, exp, and iat are present.
  • The username claim is valid.

Results are not cached. A rejected token gets one generic 401 and an external-token-rejected audit log record with the reason, never the token.


Environment Variables​

The following environment variables configure OAuth behavior:

AUTH​

The method the MCP server uses to authenticate to the Tableau REST APIs.

  • Defaults to oauth when OAuth is enabled.
  • Can still be set to other authentication methods. See Authentication for options.
  • When set to a value other than oauth, the MCP server will still be protected from unauthorized access by OAuth but will not use the Tableau session initiated by the Tableau OAuth flow to authenticate to the Tableau REST APIs. For example, if AUTH is set to uat, the MCP server will use the Unified Access Token (UAT) configuration to authenticate to the Tableau REST APIs.

OAUTH_ISSUER​

Setting this environment variable enables OAuth. This should be the origin of your MCP server (the issuer of access tokens).

When you use your own identity provider, set it to the provider's issuer exactly as it appears in the token's iss claim. A trailing slash matters. With OAUTH_EXTERNAL_DISCOVERY, it must use https unless the host is loopback.

  • Example: http://127.0.0.1:3927 (for local testing) or https://tableau-mcp.example.com (for production)
  • Required if AUTH is oauth
  • Required if TRANSPORT is http unless opted out with DANGEROUSLY_DISABLE_OAUTH

TRANSPORT​

The MCP transport type to use for the server.

  • Defaults to http when OAuth is enabled.
  • Must be http when OAuth is enabled.

SERVER​

  • When AUTH is oauth, leave this empty to support any Tableau Cloud pod determined by the site the user signed into when connecting to the MCP server.

SITE_NAME​

The target Tableau site for OAuth. The user must sign in to this site unless OAUTH_LOCK_SITE is false.

  • Use the site's Content URL (not the display name). You can find this in the site URL or in the Tableau Server/Cloud admin settings.
  • The Content URL may differ from the display name (e.g., Content URL Internal vs display name [INTERNAL] My Company). Either value will be accepted when verifying the site, but Content URL is recommended since it is used in Tableau's OAuth flow.

OAUTH_LOCK_SITE​

Whether to require the user to sign in to the site specified in SITE_NAME when using OAuth.

  • Default: true
  • When true, the user must sign in to the site specified in SITE_NAME (or the Default site if empty).
    • If the user already has an active Tableau session in their browser for a different site, an error will be returned.
  • When false, the user can sign in to any site they can access.

OAUTH_REDIRECT_URI​

The redirect URI for the OAuth flow.

  • Default: ${OAUTH_ISSUER}/Callback
  • Recommended to not define a value at all and just rely on its default value.
  • Path must be /Callback (case-sensitive).
info

Tableau Server administrators must also use tsm to set oauth.allowed_redirect_uri_hosts to the host of the MCP server.

The value should be the same as OAUTH_ISSUER but without the protocol or any trailing slash.

tsm configuration set -k oauth.allowed_redirect_uri_hosts -v tableau-mcp.example.com
tsm pending-changes apply

OAUTH_RESOURCE_URI​

The base URL used in the resource field of the OAuth protected resource metadata document. Some clients may require the resource field to match exactly with the URL used to access the MCP server. This should be the base URL of your MCP server deployment.

This value is the deployment domain only (no path). The MCP server is reached at <OAUTH_RESOURCE_URI>/tableau-mcp, which is the canonical resource identifier advertised as resource in the protected resource metadata document. Incoming bearer tokens are audience-validated against it: the token's aud claim must equal <OAUTH_RESOURCE_URI>/tableau-mcp, per RFC 9068 and RFC 8707. A token whose aud matches neither that value nor OAUTH_GLOBAL_RESOURCE_URIS is rejected with a 401.

  • Default: http://127.0.0.1:3927
  • Example: http://127.0.0.1:3927 (for local testing) or https://tableau-mcp.example.com (for production)

OAUTH_GLOBAL_RESOURCE_URIS​

One or more additional values that are also accepted in a token's aud claim. Use this when clients may reach the deployment through an environment-wide global URL in addition to the pod-specific OAUTH_RESOURCE_URI.

  • Optional. When unset, only the OAUTH_RESOURCE_URI value is accepted.
  • Accepts a single value or a comma-separated list of values.
  • Each value is matched against aud, so set it to the value the authorization server stamps into the claim (for example https://mcp.tableau.com). With your own identity provider, a value may be this MCP server's own client ID, to accept ID tokens.
  • Applies only when using a Tableau authorization server or your own identity provider (OAUTH_EMBEDDED_AUTHZ_SERVER is false).
  • Each value must be unique to this MCP server: any token whose aud matches is accepted.

OAUTH_CLIENT_ID_SECRET_PAIRS​

A comma-separated list of client ID and secret pairs to be used for confidential OAuth clients that provide client credentials during token requests.

  • Optional.
  • Example: client1:secret1,client2:secret2
  • Client IDs and secrets must be unique and cannot contain colons or commas.
  • The /oauth2/token endpoint accepts client credentials in the request body or the authorization header. If both are provided, the request body takes precedence.
  • When unspecified, in the authorization server metadata:
    • The grant_types_supported field will not include client_credentials.
    • The token_endpoint_auth_methods_supported field will not include client_secret_basic or client_secret_post.

Example /oauth2/token request body:

{
"grant_type": "client_credentials",
"client_id": "clientId",
"client_secret": "secret"
}

Example /oauth2/token request header:

Authorization: Basic Y2xpZW50SWQ6c2VjcmV0

Where Y2xpZW50SWQ6c2VjcmV0 is the base64 encoding of clientId:secret.


OAUTH_DISABLE_SCOPES​

Disable scope enforcement and scope challenges.

  • Default: false
  • Useful for Tableau Server deployments that do not want to enforce scopes yet.
  • When true, the MCP server will not include scopes in WWW-Authenticate challenges and will not enforce scopes on incoming access tokens.
  • With your own identity provider, true is required to accept ID tokens, which carry no scopes. It is safe only when every accepted audience is unique to this MCP server.

OAUTH_JWE_PRIVATE_KEY​

The RSA private key used to decrypt the OAuth access token.

  • It or OAUTH_JWE_PRIVATE_KEY_PATH must be provided, but not both.

  • Only PEM format is supported.

  • Examples:

    -----BEGIN RSA PRIVATE KEY-----\nMIIE...HZ3Q==\n-----END RSA PRIVATE KEY-----

    or

    -----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIJ...te1w==\n-----END ENCRYPTED PRIVATE KEY-----
info

The access token issued by the MCP server is encrypted using JWE (JSON Web Encryption) using an RSA public key. This public key is derived from the provided RSA private key.

MCP clients provide this encrypted access token to the MCP server on the Authorization header of its requests. The MCP server decrypts the access token using the provided private key and uses the Tableau access token held within to authenticate subsequent requests to Tableau APIs. Any requests to the MCP server that do not have a valid access token will be rejected.

If you need a private key, you can generate one using openssl-genrsa e.g.

openssl genrsa -out private.pem

OAUTH_JWE_PRIVATE_KEY_PATH​

The absolute path to the RSA private key (.pem) file used to decrypt the OAuth access token.

  • It or OAUTH_JWE_PRIVATE_KEY must be provided, but not both.
  • Only PEM format is supported.

OAUTH_JWE_PRIVATE_KEY_PASSPHRASE​

The passphrase for the private key if it is encrypted.


OAUTH_CIMD_DNS_SERVERS​

The Tableau MCP server supports MCP clients that register using a Client ID Metadata Document (CIMD) URL. Part of this process requires resolving the IP address of the host of the document to protect against DNS rebinding and Server-Side Request Forgery (SSRF) attacks.

By default, the MCP server will use Cloudflare's Public DNS (1.1.1.1 and 1.0.0.1) but you can override this using the OAUTH_CIMD_DNS_SERVERS environment variable.

  • Default: 1.1.1.1,1.0.0.1
  • Format is a comma-separated list of IP addresses.
  • Example: 8.8.8.8,8.8.4.4 (Google's Public DNS)

References:


OAUTH_EMBEDDED_AUTHZ_SERVER​

A hint that OAUTH_ISSUER points at the embedded OAuth authorization server (authorize, token, callback, register routes). When false, the issuer is an external authorization server (Tableau, or your own identity provider); only .well-known endpoints are exposed and JWE key/redirect URI constraints are skipped.

  • Default: true

OAUTH_EXTERNAL_DISCOVERY​

When true, the MCP server reads your identity provider's OpenID Connect discovery document at OAUTH_ISSUER + /.well-known/openid-configuration (without a doubled / if the issuer ends in one). It then accepts JWTs signed by a key from the document's jwks_uri: access tokens (RFC 9068) or OpenID Connect ID tokens. See Use your own identity provider.

  • Default: false. Any value other than true leaves it off. If OAUTH_EXTERNAL_USERNAME_CLAIM_NAME is set without OAUTH_EXTERNAL_DISCOVERY=true, the server does not start.
  • Only RS, PS, ES, and EdDSA signatures are accepted. HS and none tokens are rejected.
  • exp and nbf allow 5 seconds of clock skew.
  • iss, sub, aud, exp, and iat must be present.
  • Keys are cached. They are fetched again when a token names an unknown key ID (kid), at most once every 30 seconds.
  • OAUTH_ISSUER must use https unless the host is loopback, and must not contain a username or password.
  • The document is read on the first request, then kept for the life of the process. If your provider moves its jwks_uri, restart the MCP server. If the read fails, the request is rejected and the next request tries again.
  • The document's issuer must equal OAUTH_ISSUER exactly, including any trailing slash.
  • jwks_uri must use https unless both its host and the OAUTH_ISSUER host are loopback. It can be on another host than the issuer.
  • Your provider must serve the document at that path.

OAUTH_EXTERNAL_USERNAME_CLAIM_NAME​

The name of the token claim to use as the Tableau username. The claim's value must be a non-empty string, or a whole number from 0 to 9007199254740991, which is used as its decimal string. sub itself must be a non-empty string, so a number works only in a custom username claim. Surrounding spaces in the name are trimmed, and an empty name falls back to sub.

  • Default: sub

SESSION_STORE_PROVIDER​

The session store provider to use for the embedded OAuth authorization server's session state (pending authorizations, authorization codes, refresh tokens, and OAuth client registrations). Only relevant when AUTH is oauth with the embedded authorization server enabled (the default; see OAUTH_EMBEDDED_AUTHZ_SERVER above).

  • Default: memory
  • Possible values:
    • memory - In-memory session store (default). State is process-local and does not survive a restart or a multi-replica deployment.
    • custom - Load a custom session store provider from a user-specified module, backed by external infrastructure (e.g. Redis, a database, or blob storage) so session state survives restarts and is shared across replicas.
Custom Provider

To use a custom session store provider, set SESSION_STORE_PROVIDER=custom and provide the module path via SESSION_STORE_PROVIDER_CONFIG:

SESSION_STORE_PROVIDER=custom
SESSION_STORE_PROVIDER_CONFIG='{"module":"./my-session-store.js"}'

The custom provider module should export a default class or named export SessionStore that implements the SessionStore interface. consume must be implemented as a truly atomic operation against the backing store (e.g. an ETag-based conditional write for S3/blob storage, a Lua script or MULTI/EXEC for Redis, or a transaction for a relational store).


SESSION_STORE_PROVIDER_CONFIG​

Configuration for custom session store providers (JSON string).

  • Required when: SESSION_STORE_PROVIDER is custom
  • Format: {"module": "<path-to-module>", ...additional-config}

The module field can be:

  • A relative file path (e.g., ./my-provider.js) - resolved from process working directory
  • An absolute file path (e.g., /path/to/provider.js)
  • An npm package name (e.g., @company/session-store-provider)

Example:

SESSION_STORE_PROVIDER_CONFIG='{"module":"./providers/redis-session-store.js"}'

Include Tableau API scopes in OAuth metadata and scope challenges.

  • Default: false
  • When true, scopes_supported includes MCP + API scopes and scope challenges may include API scopes for step-up.
  • The check for AI-powered Pulse insight tools needs tableau:entitlements:read, so it is part of the advertised set. Under strict scope matching (the default), tokens issued before upgrading must be re-consented to include it. With DISABLE_STRICT_SCOPE_MATCH true, those tools are left out instead.

DISABLE_STRICT_SCOPE_MATCH​

Do not require every access token to carry all supported scopes before it can connect. Allows for access tokens that are subsets of the full supported scopes lists to be used in a connection.

  • Default: false.
  • Has no effect when OAUTH_DISABLE_SCOPES is true.
  • When false, a token missing any supported tableau:mcp:* scope is rejected with a 403 insufficient_scope error. When ADVERTISE_API_SCOPES is true, the token must also carry the supported Tableau API scopes, except optional ones.
  • When true, a token with only some of the supported scopes can connect. Tools the token lacks scopes for are left out of the tool list, and the server's initialize instructions name them.
  • When true, ADVERTISE_API_SCOPES is true, and ENABLE_MCP_SITE_SETTINGS is true (its default), every token must carry tableau:mcp_site_settings:read so the server can read the site's settings. A token without it is rejected with a 403, and the embedded authorization server refuses to issue one (400 invalid_scope).

OAUTH_AUTHORIZATION_CODE_TIMEOUT_MS​

The timeout for the OAuth authorization codes.

  • Default: 10 seconds.
  • Max: 1 hour.

OAUTH_ACCESS_TOKEN_TIMEOUT_MS​

The timeout for the OAuth access tokens.

  • Default: 1 hour.
  • Max: 30 days.

OAUTH_REFRESH_TOKEN_TIMEOUT_MS​

warning

OAuth support is currently in beta, and the present implementation retains refresh tokens in memory. Consequently, a server administrator's action of stopping and restarting the server, potentially during an upgrade, will result in the loss of all refresh tokens.

Any access tokens actively utilized by MCP clients will remain functional until their expiration, but subsequent attempts to refresh these tokens will fail, requiring the user to sign out and then sign back in.

This issue also pertains to authorization codes, though its impact is less likely due to their significantly shorter expiration window. This limitation will be addressed in a later release.

The timeout for the OAuth refresh tokens.

  • Default: 30 days.
  • Max: 1 year.

DANGEROUSLY_DISABLE_OAUTH​

warning

When TRANSPORT is http, the default behavior changes to require protecting your MCP server with OAuth as a security best practice.

To opt out of this behavior at your own risk, set DANGEROUSLY_DISABLE_OAUTH to true.

This is not recommended as your MCP server will not be protected from unauthorized access. By explicitly disabling OAuth, you accept any and all risks associated with this decision.

  • Default: false

Sequence Diagram​

Key Components​

Grant Types​

The MCP server supports three OAuth 2.1 grant types:

  1. Authorization Code Grant (with PKCE): Interactive flow requiring user authentication

    • User must authenticate with Tableau Server
    • Supports refresh tokens for long-term access
    • Uses PKCE for security
    • Suitable for interactive applications
  2. Client Credentials Grant: Non-interactive flow for service-to-service authentication

    • No user authentication required
    • No refresh tokens issued
    • Uses pre-configured client ID/secret pairs
    • Suitable for automated systems and APIs
  3. Refresh Token Grant: Used to obtain new access tokens

    • Only available for authorization code grants
    • Automatically refreshes Tableau tokens when possible
    • Falls back to existing tokens if refresh fails

Security Features​

  • PKCE (RFC 7636): Code challenge/verifier for public clients (authorization code only)
  • JWE Encryption: Access tokens encrypted with RSA-OAEP-256/A256GCM
  • State Validation: Prevents CSRF attacks (authorization code only)
  • Time-limited Tokens: Authorization codes (10 seconds), access tokens (1 hour), refresh tokens (30 days)
  • Client Credential Verification: Secure comparison using timing-safe operations

Token Types​

  1. Tableau OAuth Tokens: Direct from Tableau Server (authorization code only)
  2. MCP Authorization Code: Short-lived code for token exchange (authorization code only)
  3. MCP Access Token: JWE-encrypted token containing Tableau credentials or client info
  4. MCP Refresh Token: For obtaining new access tokens (authorization code only)

Endpoints​

  • /.well-known/oauth-protected-resource: Resource metadata discovery
  • /.well-known/oauth-authorization-server: Authorization server metadata
  • /oauth2/register: Dynamic client registration
  • /oauth2/authorize: Authorization endpoint with PKCE (authorization code only)
  • /Callback: OAuth callback handler (authorization code only)
  • /oauth2/token: Token exchange and refresh (all grant types)
  • /oauth2/revoke: Token revocation

For MCP client-facing token and consent cleanup, see the reset-consent and revoke-access-token tools. For full cleanup, call reset-consent before revoke-access-token.