Skip to content

Commit e0e8149

Browse files
authored
Document vMCP embedded auth server and upstream token injection (#643)
- Document the embedded authorization server for vMCP, including multi-upstream provider support and sequential authorization chaining - Document the `upstream_inject` outgoing auth strategy (vMCP-only) - Document `subjectProviderName` on token exchange for hybrid deployments - Update related docs to reflect multi-upstream capability and add cross-references
1 parent d367f0f commit e0e8149

8 files changed

Lines changed: 693 additions & 171 deletions

File tree

docs/toolhive/concepts/auth-framework.mdx

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -174,33 +174,19 @@ flowchart TD
174174
In the standard authentication flow described above, clients obtain tokens
175175
independently from an external identity provider and present them to ToolHive
176176
for validation. The embedded authorization server provides an alternative model
177-
where ToolHive itself acts as an OAuth authorization server, retrieving tokens
178-
from an upstream identity provider on behalf of clients.
179-
180-
:::note
181-
182-
The embedded authorization server is currently available only for Kubernetes
183-
deployments using the ToolHive Operator.
184-
185-
:::
186-
187-
From the client's perspective, the embedded authorization server provides a
188-
standard OAuth 2.0 experience:
189-
190-
1. If the client is not yet registered, it registers via Dynamic Client
191-
Registration (DCR), receiving a `client_id` and `client_secret`.
192-
2. The client is directed to the ToolHive authorization endpoint.
193-
3. ToolHive redirects the client to the upstream identity provider for
194-
authentication (for example, signing in with GitHub or Atlassian).
195-
4. ToolHive exchanges the authorization code for upstream tokens and issues its
196-
own JWT to the client, signed with keys you configure.
197-
5. The client includes this JWT as a `Bearer` token in the `Authorization`
198-
header on subsequent requests.
199-
200-
Behind the scenes, ToolHive stores the upstream tokens and uses them to
201-
authenticate MCP server requests to external APIs. For the complete flow,
202-
including token storage and forwarding, see
203-
[Embedded authorization server](./backend-auth.mdx#embedded-authorization-server).
177+
where ToolHive itself acts as an OAuth authorization server, obtaining tokens
178+
from an upstream provider on behalf of clients—then storing those tokens and
179+
issuing its own JWTs for clients to use on subsequent requests.
180+
181+
This solves two problems at once: it eliminates the client registration burden
182+
through Dynamic Client Registration (DCR), and it bridges the gap for external
183+
APIs like GitHub or Atlassian where no federation relationship exists with your
184+
identity provider.
185+
186+
For the complete conceptual description—including the OAuth flow, token storage
187+
and forwarding, session storage options, and differences between MCPServer and
188+
VirtualMCPServer—see
189+
[Embedded authorization server](./embedded-auth-server.mdx).
204190

205191
For Kubernetes setup instructions, see
206192
[Set up embedded authorization server authentication](../guides-k8s/auth-k8s.mdx#set-up-embedded-authorization-server-authentication).

docs/toolhive/concepts/backend-auth.mdx

Lines changed: 9 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -159,145 +159,26 @@ When the MCP server needs to call an external API where no federation
159159
relationship exists—such as GitHub, Google Workspace, or Atlassian APIs—the
160160
embedded authorization server handles the full OAuth web flow against the
161161
external provider. The proxy redirects the user to authenticate directly with
162-
the external service, obtains tokens on behalf of the user, and passes the
163-
upstream token to the MCP server.
164-
165-
```mermaid
166-
sequenceDiagram
167-
participant User
168-
participant Proxy as ToolHive Proxy
169-
participant ExtProvider as External Provider
170-
171-
User->>Proxy: Connect
172-
Proxy-->>User: Redirect to login
173-
User->>ExtProvider: Authenticate
174-
ExtProvider->>Proxy: Authorization code
175-
Proxy->>ExtProvider: Exchange code for token
176-
ExtProvider->>Proxy: Upstream tokens
177-
Proxy->>User: Issue JWT
178-
```
179-
180-
On subsequent MCP requests, ToolHive uses the JWT to retrieve the stored
181-
upstream tokens and forward them to the MCP server. For details on this
182-
mechanism, see [Token storage and forwarding](#token-storage-and-forwarding).
162+
the external service, obtains tokens on behalf of the user, and automatically
163+
forwards the upstream token to the MCP server on each subsequent request.
183164

184165
The embedded authorization server runs in-process within the ToolHive proxy—no
185166
separate infrastructure is needed. It supports Dynamic Client Registration
186167
(DCR), so MCP clients can register automatically with ToolHive—no manual client
187168
configuration in ToolHive is required.
188169

170+
For a full explanation of how the OAuth flow works, token storage and
171+
forwarding, automatic token refresh, session storage options, and differences
172+
between MCPServer and VirtualMCPServer deployments, see
173+
[Embedded authorization server](./embedded-auth-server.mdx).
174+
189175
:::note
190176

191177
The embedded authorization server is currently available only for Kubernetes
192178
deployments using the ToolHive Operator.
193179

194180
:::
195181

196-
#### Key characteristics
197-
198-
- **In-process execution:** The authorization server runs within the ToolHive
199-
proxy—no separate infrastructure or sidecar containers needed.
200-
- **Configurable signing keys:** JWTs are signed with keys you provide,
201-
supporting key rotation for zero-downtime updates.
202-
- **Flexible upstream providers:** Supports both OIDC providers (with automatic
203-
endpoint discovery) and OAuth 2.0 providers (with explicit endpoint
204-
configuration).
205-
- **Configurable token lifespans:** Access tokens, refresh tokens, and
206-
authorization codes have configurable durations with sensible defaults.
207-
- **Dynamic Client Registration (DCR):** Supports OAuth 2.0 Dynamic Client
208-
Registration (RFC 7591), allowing MCP clients to register automatically with
209-
ToolHive's authorization server—no manual client registration in ToolHive is
210-
required.
211-
- **Direct upstream redirect:** The embedded authorization server redirects
212-
clients directly to the upstream provider for authentication (for example,
213-
GitHub or Atlassian).
214-
- **Single upstream provider:** Currently supports one upstream identity
215-
provider per configuration.
216-
217-
:::info[Chained authentication not yet supported]
218-
219-
The embedded authorization server redirects clients directly to the upstream
220-
provider. This means the upstream provider must be the service whose API the MCP
221-
server calls. Chained authentication—where a client authenticates with a
222-
corporate IdP like Okta, which then federates to an external provider like
223-
GitHub—is not yet supported. If your deployment requires this pattern, consider
224-
using [token exchange](#same-idp-with-token-exchange) with a federated identity
225-
provider instead.
226-
227-
:::
228-
229-
#### Token storage and forwarding
230-
231-
The embedded authorization server stores upstream tokens (access tokens, refresh
232-
tokens, and ID tokens from external providers) in session storage. When the
233-
OAuth flow completes, the server generates a unique session ID and stores the
234-
upstream tokens keyed by this ID. The JWT issued to the client contains a `tsid`
235-
(Token Session ID) claim that references this session.
236-
237-
When a client makes an MCP request with this JWT:
238-
239-
1. The ToolHive proxy validates the JWT signature and extracts the `tsid` claim
240-
2. It retrieves the upstream tokens from session storage using the `tsid`
241-
3. The proxy replaces the `Authorization` header with the upstream access token
242-
4. The request is forwarded to the MCP server with the external provider's token
243-
244-
```mermaid
245-
sequenceDiagram
246-
participant Client
247-
participant Proxy as ToolHive Proxy
248-
participant Store as Session Storage
249-
participant MCP as MCP Server
250-
participant API as External API
251-
252-
Note over Client,Store: Initial OAuth flow
253-
Proxy->>Store: Store upstream tokens<br/>keyed by session ID
254-
Proxy-->>Client: Issue JWT with tsid claim
255-
256-
Note over Client,API: Subsequent MCP requests
257-
Client->>Proxy: MCP request with JWT
258-
Proxy->>Proxy: Validate JWT signature
259-
Proxy->>Store: Look up upstream token<br/>using tsid from JWT
260-
Store-->>Proxy: Return upstream access token
261-
Proxy->>MCP: Forward request with<br/>upstream access token
262-
MCP->>API: Call external API
263-
API-->>MCP: Response
264-
MCP-->>Proxy: Response
265-
Proxy-->>Client: Response
266-
```
267-
268-
This mechanism allows MCP servers to call external APIs with the user's actual
269-
credentials from the upstream provider, while the client only needs to manage a
270-
single ToolHive-issued JWT.
271-
272-
#### Automatic token refresh
273-
274-
Upstream access tokens have their own expiration, independent of the ToolHive
275-
JWT lifespan. When the stored upstream access token has expired, ToolHive
276-
automatically refreshes it using the stored refresh token before forwarding the
277-
request — your MCP session continues without re-authentication.
278-
279-
If the refresh token is also expired or has been revoked by the upstream
280-
provider, ToolHive returns a `401` response, prompting you to re-authenticate
281-
through the OAuth flow.
282-
283-
:::warning[Session storage limitations]
284-
285-
By default, session storage is in-memory only. Upstream tokens are lost when
286-
pods restart, requiring users to re-authenticate. For production deployments,
287-
configure Redis Sentinel as the storage backend for persistent, highly available
288-
session storage. See
289-
[Configure session storage](../guides-k8s/auth-k8s.mdx#configure-session-storage)
290-
for a quick setup, or the full
291-
[Redis Sentinel session storage](../guides-k8s/redis-session-storage.mdx)
292-
tutorial for an end-to-end walkthrough.
293-
294-
:::
295-
296-
For the client-facing OAuth flow, see
297-
[Embedded authorization server](./auth-framework.mdx#embedded-authorization-server).
298-
For Kubernetes setup instructions, see
299-
[Set up embedded authorization server authentication](../guides-k8s/auth-k8s.mdx#set-up-embedded-authorization-server-authentication).
300-
301182
## Token exchange in depth
302183

303184
This section provides implementation details for the token exchange patterns
@@ -467,8 +348,8 @@ setup guide.
467348

468349
- For client authentication concepts, see
469350
[Authentication and authorization](./auth-framework.mdx)
470-
- For the embedded authorization server, see
471-
[Embedded authorization server](./auth-framework.mdx#embedded-authorization-server)
351+
- For a deep dive into the embedded authorization server, see
352+
[Embedded authorization server](./embedded-auth-server.mdx)
472353
- For configuring the embedded authorization server in Kubernetes, see
473354
[Set up embedded authorization server authentication](../guides-k8s/auth-k8s.mdx#set-up-embedded-authorization-server-authentication)
474355
- For configuring token exchange, see

0 commit comments

Comments
 (0)