Add passthrough auth mode for gateway/proxy deployments#234
Add passthrough auth mode for gateway/proxy deployments#234karimtabet wants to merge 2 commits intotableau:mainfrom
Conversation
|
Thanks for the contribution! Before we can merge this, we need @karimtabet to sign the Salesforce Inc. Contributor License Agreement. |
There was a problem hiding this comment.
Pull request overview
This PR adds a new passthrough authentication mode that enables the MCP server to work in enterprise gateway/proxy deployments where users are pre-authenticated by an upstream proxy. The proxy forwards valid Tableau REST API tokens via HTTP headers, and the MCP server trusts these credentials without performing additional authentication.
Changes:
- Added
passthroughas a valid auth type with automatic transport detection to HTTP - Implemented header-based credential extraction and validation middleware
- Extended REST API credential handling to support pre-authenticated tokens for both OAuth and passthrough modes
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/config.ts | Added 'passthrough' auth type, auto-detects transport as HTTP, exempts from OAUTH_ISSUER requirement, validates HTTP-only transport |
| src/config.test.ts | Added 7 comprehensive tests for passthrough configuration validation |
| src/server/middleware.ts | Added validatePassthroughHeaders middleware that returns 401 when required headers are missing |
| src/server/middleware.test.ts | Added 4 tests covering all validation scenarios for passthrough headers |
| src/server/express.ts | Added getPassthroughAuthInfo function, wired up middleware, added X-Tableau-User-Id to CORS headers |
| src/restApiInstance.ts | Refactored to check for pre-authenticated credentials before auth-type switch, skip signOut for passthrough |
| src/restApiInstance.test.ts | Added test verifying setCredentials usage for passthrough auth |
| src/testSetup.ts | Added setCredentials mock to RestApi for test support |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Add a new `AUTH=passthrough` mode that allows a trusted upstream proxy/gateway to pass pre-authenticated Tableau credentials via HTTP headers (`Authorization: Bearer <token>` and `X-Tableau-User-Id`). This fills a gap for enterprise deployments where Tableau Server doesn't yet support OAuth but users already have authenticated sessions via Kerberos or other mechanisms. The proxy acquires a Tableau REST API token on behalf of the user and forwards it to the MCP server, which uses it directly via `restApi.setCredentials()` — the same code path OAuth already uses. Changes: - config.ts: Add 'passthrough' to valid auth types, auto-detect transport as 'http', skip OAUTH_ISSUER requirement, validate TRANSPORT must be 'http' - server/express.ts: Extract credentials from Authorization and X-Tableau-User-Id headers, add headers to CORS allowedHeaders, wire up passthrough middleware - restApiInstance.ts: Check for pre-authenticated credentials before auth-type switch (subsumes OAuth path), skip sign-out for passthrough - server/middleware.ts: Add validatePassthroughHeaders middleware returning 401 on missing headers - Tests: 12 new tests covering config validation, middleware header checks, and restApiInstance credential handling Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
63c133c to
14b9f47
Compare
|
Hey @karimtabet , thanks for the PR! This is something we are working on internally, which is one of the reasons we request folks create an issue before opening a PR. The prototype branch is here: https://github.com/tableau/tableau-mcp/commits/anyoung/auth-tableau/ which is a bit behind the main branch, but the commits look pretty similar to your changes. We are also working on adding support for using Tableau as the OAuth authorization server which adds some additional complexity. I'll discuss these changes with the team to ensure we want to support this scenario. I am inclined to bring my prototype branch up to speed and do some additional testing, ensuring it covers the use case described in this PR. Does that sound reasonable? |
|
Hey @anyoung-tableau thanks for the swift reply. Yep that sounds reasonable. I'm running with a fork atm in my production environment but, seeing how fast your project is moving, would rather use origin and not have too much drift. Any idea when we might get your branch rolled out? |
I'm going to look into it this week. |
src/server/express.ts
Outdated
| return undefined; | ||
| } | ||
|
|
||
| const authHeader = req.headers['authorization']; |
There was a problem hiding this comment.
@karimtabet would you be willing to pass the access token on the X-Tableau-Auth header instead of the Authorization header? The scenario we want to support is one where we don't introduce a new passthrough value for config.auth, but for requests that have the X-Tableau-Auth we ignore the normal auth mechanism and let the value of X-Tableau-Auth pass through. That way clients that don't provide the X-Tableau-Auth header will use PAT/Direct Trust/OAuth/etc and clients that do provide the header will use the provided access token. Thoughts?
There was a problem hiding this comment.
Hey @karimtabet , please let me know your thoughts, otherwise I will move forward with the X-Tableau-Auth header approach seen in #241
There was a problem hiding this comment.
I let karim know to have a look, your comments seem sensible
There was a problem hiding this comment.
Hey @anyoung-tableau, sorry for the delay! I've updated the PR to use the X-Tableau-Auth header approach as you suggested — removed the dedicated passthrough auth type and replaced it with an ENABLE_PASSTHROUGH_AUTH flag that works alongside existing auth modes. Let me know if this aligns with what you had in mind, happy to adjust.
There was a problem hiding this comment.
Thanks @karimtabet, last thing I wanted to check... are you already utilizing or planning to utilize these changes soon in some way? I'd like to move forward, but only if there's an active use case, otherwise it's idle code we need to maintain.
There was a problem hiding this comment.
Yep, we want to run the tableau in a multi-tenant way, where no interactive flow is necessary in order to auth with tableau. This is useful in scenarios where you have headless agents that you want to spin up at scale without user interaction. In our case we do something fairly complicated to fetch PAT's in a headless manner (fetch session token via kerberos, then use session token to create a pat). But this would be the last missing piece for us to run this as desired. The alternative is we would fork and create our own artefacts, but we operate in an airgapped environment so thats a lot trickier than it should be.
There was a problem hiding this comment.
We use this type of flow for all other mcps we deploy already, tableau being one of the ones that don't currently support passthrough flows
There was a problem hiding this comment.
Cool, thanks guys. Would you be willing to close this PR in favor of us moving forward with #241 instead?
Replace dedicated 'passthrough' auth type with an ENABLE_PASSTHROUGH_AUTH flag that works alongside existing auth modes. Requests with an X-Tableau-Auth header (or workgroup_session_id cookie) bypass normal auth and are validated against the Tableau REST API's /sessions/current endpoint, with results cached.
|
Hey @karimtabet can you clarify the use case that you're building and why it needs this feature? Is it a dashboard extension? |
Some context here (i work with karim) No dashoard extension, but seamless agent automations for BAs i.e. asking claude a question and letting it interact with tableau (mainly reading data, but also for dashboard creation) |
|
@JoeConstant we maintain a central MCP gateway for our users where we host the Tableau MCP. The use case is to be able to pass through a user's token to the remote MCP so that it can make API calls on the users behalf. |
Summary
AUTH=passthroughmode that trusts pre-authenticated credentials from HTTP headers (Authorization: Bearer <token>+X-Tableau-User-Id: <user-luid>)restApi.setCredentials()code path (same as OAuth) — no new dependencies or Tableau API changes neededMotivation
There is currently no way for a trusted upstream proxy to pass per-user credentials to the MCP server via HTTP headers. This is the standard pattern in enterprise gateway deployments where:
This is particularly important for enterprises running Tableau Server versions that don't support OAuth (< 2025.3), where Kerberos SPNEGO authentication produces a
workgroup_session_idthat works as a standard REST API bearer token.patoauthdirect-trustpassthroughChanges
src/config.ts: Add'passthrough'to valid auth types, auto-detect transport as'http', exempt fromOAUTH_ISSUERrequirement, validate transport must be HTTPsrc/server/express.ts: AddgetPassthroughAuthInfo()to extract credentials from request headers, addX-Tableau-User-Idto CORS allowed headers, wire up passthrough validation middlewaresrc/restApiInstance.ts: Check for pre-authenticated credentials (accessToken+userId) before the auth-type switch, skip sign-out for passthrough sessionssrc/server/middleware.ts: AddvalidatePassthroughHeaders()middleware returning 401 JSON-RPC error on missing headersTest plan
setCredentialscalled (notsignIn),signOutskippedSecurity considerations
AUTH=passthroughtrusts theAuthorizationheader completely — same trust model as a reverse proxy terminating TLS or Kerberos