Skip to content

Conversation

anyoung-tableau
Copy link
Collaborator

@anyoung-tableau anyoung-tableau commented Aug 29, 2025

These changes allow server administrators to specify a JWT_PROVIDER_URL environment variable such that when AUTH is jwt, before the MCP server authenticates to the Tableau REST API, it will make a POST request to the endpoint provided in JWT_PROVIDER_URL. This endpoint must return the JSON web token to then be used to authenticate to the REST API and can include any additional, desired user attributes.

Security is pretty basic and leverages a header on the request whose value is an encrypted secret. The secret is known only by the Tableau MCP server and the JWT provider. The secret is encrypted by the Tableau MCP server using a public key. The JWT provider can then decrypt the header with the private key and ensure the secret matches what it expects.

POST request header:

x-tabmcp-jwt-provider-secret: [Encrypted value of JWT_PROVIDER_SECRET]

POST request body:

{
  username: "[email protected]", // The value of JWT_SUB_CLAIM
  scopes: ["tableau:example:scope"], // The list of scopes the JWT should have
  source: "tableau-mcp",
  resource: 'mcp-tool-name', // The name of the tool being called e.g. query-datasource
  server: "https://tableau.example.com", // The value of SERVER
  siteName: "siteName", // The value of SITE_NAME
}

Expected response:

{
  "jwt": "eyJhbGciOiJI..."
}

Example Express route handler:

async function jwtProviderRouteHandler(req, res) {
  // Read the secret header from the request
  const secret = req.headers['x-tabmcp-jwt-provider-secret'];

  // Decrypt the secret using the private key.
  // The secret was encrypted by the Tableau MCP server using the public key.
  const privateKey = crypto.createPrivateKey({
    format: 'pem',
    key: readFileSync(process.env.PRIVATE_KEY_PATH),
    passphrase: process.env.PRIVATE_KEY_PASSPHRASE,
  });

  // compactDecrypt is a function from the jose package
  // https://www.npmjs.com/package/jose
  const { plaintext } = await compactDecrypt(secret, privateKey);
  const equal = crypto.timingSafeEqual(
    plaintext,
    new TextEncoder().encode(process.env.JWT_PROVIDER_SECRET),
  );

  if (!equal) {
    res.status(401).json({
      error: 'Unauthorized',
    });
    return;
  }

  // Read the values provided by the Tableau MCP server
  const { username, scopes, source, resource, server, siteName } = req.body;

  // Add isAdmin user attribute for admin user
  const userAttributes = { isAdmin: username === '[email protected]' };

  // An example generateJwt function can be found here:
  // https://github.com/tableau/connected-apps-jwt-samples/blob/main/javascript/index.js
  const jwt = generateJwt(userAttributes);
  res.json({ jwt });
}

@Copilot Copilot AI review requested due to automatic review settings August 29, 2025 00:50
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for JWT token generation from an admin-provided endpoint. The implementation allows server administrators to specify a JWT_PROVIDER_URL environment variable that the server will call to obtain JWT tokens before authenticating to the Tableau REST API.

  • Introduces a new jwt authentication type alongside existing pat and direct-trust methods
  • Adds context parameter to all tool calls to identify which tool is requesting authentication
  • Creates a utility function to fetch JWT tokens from external providers with validation

Reviewed Changes

Copilot reviewed 25 out of 26 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/utils/getJwtFromProvider.ts New utility function to fetch and validate JWT tokens from external provider
src/config.ts Adds jwt auth type and JWT_PROVIDER_URL configuration with validation
src/restApiInstance.ts Integrates JWT provider functionality into REST API authentication flow
src/sdks/tableau/authConfig.ts Extends auth config types to support JWT authentication
src/tools/*/ Updates all tool files to include context parameter for authentication
src/server/express.ts Adds debug endpoint for JWT generation testing

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

app.post(path, createMcpServer);
app.get(path, methodNotAllowed);
app.delete(path, methodNotAllowed);
app.post('/jwt', generateJwt);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This endpoint is for demonstration purposes only. I'll remove it before merging.

@anyoung-tableau anyoung-tableau marked this pull request as draft August 29, 2025 02:33
@anyoung-tableau anyoung-tableau added the experimentation In-progress or experimental changes label Aug 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

experimentation In-progress or experimental changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant