diff --git a/docs/toolhive/concepts/auth-framework.mdx b/docs/toolhive/concepts/auth-framework.mdx
index 63ed4e1d..e033580d 100644
--- a/docs/toolhive/concepts/auth-framework.mdx
+++ b/docs/toolhive/concepts/auth-framework.mdx
@@ -63,18 +63,20 @@ significant operational challenges:
its own token validation and scope management, duplicating security-critical
logic across servers.
-ToolHive addresses these challenges by centralizing authentication and
-authorization in its proxy layer. You configure ToolHive with your identity
-provider and write Cedar policies for fine-grained authorization—individual MCP
-servers don't need to implement token validation or scope management.
-
-With the [embedded authorization server](#embedded-authorization-server),
-ToolHive can also manage interactive token acquisition. The proxy exposes
-standard OAuth endpoints and handles the full OAuth web flow—clients don't need
-to obtain or manage tokens externally. ToolHive delegates authentication to an
-upstream identity provider and issues its own tokens, giving MCP clients a
-spec-compliant OAuth experience while centralizing the complexity of client
-registration and token management.
+ToolHive addresses the per-server implementation cost by centralizing
+authentication and authorization in its proxy layer. You configure ToolHive with
+your identity provider and write Cedar policies for fine-grained
+authorization—individual MCP servers don't need to implement token validation or
+scope management.
+
+The [embedded authorization server](#embedded-authorization-server) addresses
+the remaining challenges. It exposes standard OAuth endpoints and handles the
+full OAuth web flow, eliminating the client registration burden through Dynamic
+Client Registration (DCR) and solving the federation gap by obtaining tokens
+directly from external providers like GitHub or Atlassian. ToolHive delegates
+authentication to the upstream provider and issues its own tokens, giving MCP
+clients a spec-compliant OAuth experience while centralizing the complexity of
+token acquisition and management.
## Authentication framework
@@ -182,94 +184,27 @@ deployments using the ToolHive Operator.
:::
-This approach is designed for MCP servers that accept `Authorization: Bearer`
-tokens and is particularly useful when you want ToolHive to handle the full
-OAuth flow rather than requiring clients to obtain tokens independently.
-
-#### How the embedded authorization server works
-
-The embedded authorization server runs in-process within the ToolHive proxy.
-When a client connects, the following flow occurs:
+From the client's perspective, the embedded authorization server provides a
+standard OAuth 2.0 experience:
1. If the client is not yet registered, it registers via Dynamic Client
Registration (DCR), receiving a `client_id` and `client_secret`.
2. The client is directed to the ToolHive authorization endpoint.
-3. The proxy redirects the client to the upstream identity provider for
- authentication.
-4. The user authenticates with the upstream identity provider (for example,
- signing in with Google or GitHub).
-5. The upstream identity provider redirects back to the proxy with an
- authorization code.
-6. The embedded authorization server exchanges the authorization code for tokens
- with the upstream identity provider.
-7. The embedded authorization server issues its own JWT to the client, signed
- with keys you configure.
-8. The client includes this JWT as a `Bearer` token in the `Authorization`
+3. ToolHive redirects the client to the upstream identity provider for
+ authentication (for example, signing in with GitHub or Atlassian).
+4. ToolHive exchanges the authorization code for upstream tokens and issues its
+ own JWT to the client, signed with keys you configure.
+5. The client includes this JWT as a `Bearer` token in the `Authorization`
header on subsequent requests.
-9. The proxy validates the JWT, retrieves the upstream token, and forwards
- requests to the MCP server.
-```mermaid
-sequenceDiagram
- participant Client
- participant Proxy as ToolHive Proxy
- participant IdP as Upstream IdP
- participant MCP as MCP Server
-
- Client->>Proxy: POST /oauth/register (DCR)
- Proxy-->>Client: client_id + client_secret
- Client->>Proxy: Connect to MCP server
- Proxy-->>Client: Redirect to /oauth/authorize
- Client->>Proxy: GET /oauth/authorize
- Proxy-->>Client: Redirect to upstream IdP
- Client->>IdP: Authenticate
- IdP-->>Client: Redirect with authorization code
- Client->>Proxy: GET /oauth/callback?code=...
- Proxy->>IdP: Exchange code for tokens
- IdP-->>Proxy: Upstream tokens
- Proxy-->>Client: Issue ToolHive JWT
- Client->>Proxy: MCP request with Bearer token
- Proxy->>Proxy: Validate JWT
- Proxy->>MCP: Forward request
- MCP-->>Proxy: Response
- Proxy-->>Client: Response
-```
-
-#### Key characteristics
-
-- **In-process execution:** The authorization server runs within the ToolHive
- proxy—no separate infrastructure or sidecar containers needed.
-- **Configurable signing keys:** JWTs are signed with keys you provide,
- supporting key rotation for zero-downtime updates.
-- **Flexible upstream providers:** Supports both OIDC providers (with automatic
- endpoint discovery) and OAuth 2.0 providers (with explicit endpoint
- configuration).
-- **Configurable token lifespans:** Access tokens, refresh tokens, and
- authorization codes have configurable durations with sensible defaults.
-- **Dynamic Client Registration (DCR):** Supports OAuth 2.0 Dynamic Client
- Registration (RFC 7591), allowing MCP clients to register automatically
- without manual configuration at the identity provider.
-- **Direct upstream redirect:** The embedded authorization server redirects
- clients directly to the upstream provider for authentication (for example,
- GitHub or Atlassian).
-- **Single upstream provider:** Currently supports one upstream identity
- provider per configuration.
-
-:::info[Chained authentication not yet supported]
-
-The embedded authorization server redirects clients directly to the upstream
-provider. This means the upstream provider must be the service whose API the MCP
-server calls. Chained authentication—where a client authenticates with a
-corporate IdP like Okta, which then federates to an external provider like
-GitHub—is not yet supported. If your deployment requires this pattern, consider
-using [token exchange](./backend-auth.mdx#same-idp-with-token-exchange) with a
-federated identity provider instead.
-
-:::
+Behind the scenes, ToolHive stores the upstream tokens and uses them to
+authenticate MCP server requests to external APIs. For the complete flow
+including how upstream tokens are stored and delivered to MCP servers, see
+[Embedded authorization server](./backend-auth.mdx#embedded-authorization-server)
+in the backend authentication documentation.
-For guidance on choosing the right backend authentication pattern for your MCP
-servers, see
-[Choosing the right backend authentication model](./backend-auth.mdx#choosing-the-right-backend-authentication-model).
+For Kubernetes setup instructions, see
+[Set up embedded authorization server authentication](../guides-k8s/auth-k8s.mdx#set-up-embedded-authorization-server-authentication).
### Identity providers
diff --git a/docs/toolhive/concepts/backend-auth.mdx b/docs/toolhive/concepts/backend-auth.mdx
index 359f31c3..ff808f81 100644
--- a/docs/toolhive/concepts/backend-auth.mdx
+++ b/docs/toolhive/concepts/backend-auth.mdx
@@ -2,13 +2,14 @@
title: Backend authentication
description:
Understanding how MCP servers authenticate to external services using
- ToolHive's token exchange framework.
+ ToolHive's backend authentication patterns, including static credentials,
+ token exchange, and the embedded authorization server.
---
This document explains how ToolHive helps MCP servers authenticate to
third-party APIs and backend services exposed through the MCP servers. You'll
-learn about the backend authentication patterns ToolHive supports, why its
-approach improves security and multi-tenancy, and how it simplifies MCP server
+learn about the backend authentication patterns ToolHive supports, why they
+improve security and multi-tenancy, and how they simplify MCP server
development.
:::info[Scope of this documentation]
@@ -40,40 +41,53 @@ authentication code. This creates several problems:
- **Multi-tenancy complexity:** Supporting multiple tenants with isolated
credentials requires significant custom code
-ToolHive addresses these challenges by implementing several authentication
-patterns such as token exchange and federated identity that provide MCP servers
-with properly scoped, short-lived access tokens instead of requiring embedded
-secrets.
+ToolHive addresses these challenges with three backend authentication patterns:
+static credentials for services that don't support OAuth, token exchange for
+services in the same or federated trust domain, and the embedded authorization
+server for OAuth-based external APIs where no federation exists.
-## Managing third-party service tokens
+## How ToolHive handles backend authentication
-The key insight is that ToolHive already implements authentication and
-authorization for the MCP server—it helps the client authenticate and obtain an
-access token scoped for the MCP server. ToolHive can leverage this existing
-token to obtain a token for the backend service and pass it to the MCP server.
+ToolHive sits between clients and MCP servers, and can acquire backend
+credentials on behalf of the MCP server. Depending on the pattern, it might
+exchange the client's token, run an OAuth flow against an external provider, or
+inject static credentials. In each case, the MCP server receives ready-to-use
+credentials—via an `Authorization: Bearer` header, another header, or
+environment variables, depending on the pattern—without needing to implement
+custom authentication logic or manage secrets directly.
-This means the MCP server receives a properly scoped token for the external
-service as part of the standard MCP protocol call, typically in the
-`Authorization: Bearer` header. This simplifies MCP server implementation
-because:
+## Backend authentication patterns
-- No long-lived tokens need to be stored
-- No custom authentication code is required
-- The MCP server can focus on its business logic
-- Each request is attributed to the individual user making it
+ToolHive supports three patterns for backend authentication. Which one you use
+depends on the relationship between your identity provider (IdP) and the backend
+service.
-## Methods for acquiring external access tokens
+### Static credentials and API keys
-ToolHive supports multiple patterns for obtaining external access tokens,
-depending on the relationship between your identity provider (IdP) and the
-external service. All patterns assume OAuth-based authentication. For services
-using other authentication methods (such as database connections with static
-credentials), consider integrations with secret management systems like
-[HashiCorp Vault](../tutorials/vault-integration.mdx).
+When a backend service requires API keys, database passwords, or other static
+credentials, you can configure them directly in ToolHive as environment
+variables, secret files, or injected headers.
-ToolHive implements two main patterns:
+This is the simplest pattern, but it provides the least security and
+auditability. Static credentials are long-lived and shared across all users, so
+there is no per-user attribution in audit logs.
-### Same IdP with token exchange
+When using static credentials, consider integrating with a secret management
+system like [HashiCorp Vault](../tutorials/vault-integration.mdx) for secure
+storage and rotation.
+
+### Token exchange
+
+When the backend service trusts the same IdP as your MCP clients—or federation
+is configured between the two IdPs—ToolHive can exchange the client's token for
+one scoped to the backend service using
+[RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) token exchange. This
+preserves the user's identity across services and provides short-lived, narrowly
+scoped tokens. Because the trust relationship between services is pre-configured
+at the IdP, no consent screen or user interaction is required—the exchange is
+transparent to the end user.
+
+#### Same IdP with token exchange
When both the MCP server and the backend service trust the same IdP, and that
IdP supports [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) token
@@ -106,7 +120,7 @@ flowchart LR
4. ToolHive passes this access token to the MCP server
5. The MCP server uses the access token to call the upstream service
-### Federated IdPs with identity mapping
+#### Federated IdPs with identity mapping
When the backend service trusts a different IdP, but federation is configured
between the two IdPs, ToolHive can use the federated identity service to issue
@@ -140,7 +154,150 @@ flowchart LR
4. ToolHive passes token_B to the MCP server
5. The MCP server uses token_B to call the upstream service
-## Token exchange implementation
+### Embedded authorization server
+
+When the MCP server needs to call an external API where no federation
+relationship exists—such as GitHub, Google Workspace, or Atlassian APIs—the
+embedded authorization server handles the full OAuth web flow against the
+external provider. The proxy redirects the user to authenticate directly with
+the external service, obtains tokens on behalf of the user, and passes the
+upstream token to the MCP server.
+
+```mermaid
+flowchart LR
+ User[User]
+ Proxy[ToolHive Proxy]
+ ExtProvider[External Provider]
+
+ User -->|connect| Proxy
+ Proxy -->|redirect to login| ExtProvider
+ User -->|authenticate| ExtProvider
+ ExtProvider -->|authorization code| Proxy
+ Proxy -->|exchange code for token| ExtProvider
+ Proxy -->|issue JWT| User
+```
+
+**How it works:**
+
+1. The client connects to ToolHive
+2. ToolHive redirects the user to the external provider (for example, GitHub)
+ for authentication
+3. The user authenticates with the external provider
+4. ToolHive receives an authorization code and exchanges it for upstream tokens
+5. ToolHive stores the upstream tokens and issues its own JWT to the client
+
+On subsequent MCP requests, ToolHive uses the JWT to retrieve the stored
+upstream tokens and forward them to the MCP server. For details on this
+mechanism, see [Token storage and forwarding](#token-storage-and-forwarding).
+
+The embedded authorization server runs in-process within the ToolHive proxy—no
+separate infrastructure is needed. It supports Dynamic Client Registration
+(DCR), so MCP clients can register automatically with ToolHive—no manual client
+configuration in ToolHive is required.
+
+:::note
+
+The embedded authorization server is currently available only for Kubernetes
+deployments using the ToolHive Operator.
+
+:::
+
+#### Key characteristics
+
+- **In-process execution:** The authorization server runs within the ToolHive
+ proxy—no separate infrastructure or sidecar containers needed.
+- **Configurable signing keys:** JWTs are signed with keys you provide,
+ supporting key rotation for zero-downtime updates.
+- **Flexible upstream providers:** Supports both OIDC providers (with automatic
+ endpoint discovery) and OAuth 2.0 providers (with explicit endpoint
+ configuration).
+- **Configurable token lifespans:** Access tokens, refresh tokens, and
+ authorization codes have configurable durations with sensible defaults.
+- **Dynamic Client Registration (DCR):** Supports OAuth 2.0 Dynamic Client
+ Registration (RFC 7591), allowing MCP clients to register automatically with
+ ToolHive's authorization server—no manual client registration in ToolHive is
+ required.
+- **Direct upstream redirect:** The embedded authorization server redirects
+ clients directly to the upstream provider for authentication (for example,
+ GitHub or Atlassian).
+- **Single upstream provider:** Currently supports one upstream identity
+ provider per configuration.
+
+:::info[Chained authentication not yet supported]
+
+The embedded authorization server redirects clients directly to the upstream
+provider. This means the upstream provider must be the service whose API the MCP
+server calls. Chained authentication—where a client authenticates with a
+corporate IdP like Okta, which then federates to an external provider like
+GitHub—is not yet supported. If your deployment requires this pattern, consider
+using [token exchange](#same-idp-with-token-exchange) with a federated identity
+provider instead.
+
+:::
+
+#### Token storage and forwarding
+
+The embedded authorization server stores upstream tokens (access tokens, refresh
+tokens, and ID tokens from external providers) in session storage. When the
+OAuth flow completes, the server generates a unique session ID and stores the
+upstream tokens keyed by this ID. The JWT issued to the client contains a `tsid`
+(Token Session ID) claim that references this session.
+
+When a client makes an MCP request with this JWT:
+
+1. The ToolHive proxy validates the JWT signature and extracts the `tsid` claim
+2. It retrieves the upstream tokens from session storage using the `tsid`
+3. The proxy replaces the `Authorization` header with the upstream access token
+4. The request is forwarded to the MCP server with the external provider's token
+
+```mermaid
+sequenceDiagram
+ participant Client
+ participant Proxy as ToolHive Proxy
+ participant Store as Session Storage
+ participant MCP as MCP Server
+ participant API as External API
+
+ Note over Client,Store: Initial OAuth flow
+ Proxy->>Store: Store upstream tokens
keyed by session ID
+ Proxy-->>Client: Issue JWT with tsid claim
+
+ Note over Client,API: Subsequent MCP requests
+ Client->>Proxy: MCP request with JWT
+ Proxy->>Proxy: Validate JWT signature
+ Proxy->>Store: Look up upstream token
using tsid from JWT
+ Store-->>Proxy: Return upstream access token
+ Proxy->>MCP: Forward request with
upstream access token
+ MCP->>API: Call external API
+ API-->>MCP: Response
+ MCP-->>Proxy: Response
+ Proxy-->>Client: Response
+```
+
+This mechanism allows MCP servers to call external APIs with the user's actual
+credentials from the upstream provider, while the client only needs to manage a
+single ToolHive-issued JWT.
+
+:::warning[Session storage limitations]
+
+In Kubernetes deployments, session storage is currently in-memory only. Upstream
+tokens are lost when pods restart, requiring users to re-authenticate.
+
+:::
+
+For the client-facing OAuth flow, see
+[Embedded authorization server](./auth-framework.mdx#embedded-authorization-server).
+For Kubernetes setup instructions, see
+[Set up embedded authorization server authentication](../guides-k8s/auth-k8s.mdx#set-up-embedded-authorization-server-authentication).
+
+## Token exchange in depth
+
+This section provides implementation details for the token exchange patterns
+described above. For setup instructions, see
+[Configure token exchange](../guides-cli/token-exchange.mdx) (CLI) or
+[Configure token exchange in Kubernetes](../guides-k8s/token-exchange-k8s.mdx).
+
+### Token exchange implementation
The token exchange flow demonstrates how ToolHive transforms user identity
tokens into properly scoped service tokens.
@@ -195,14 +352,12 @@ Notice how the audience (`aud`) and scopes (`scp`) change while preserving the
user's identity (`sub`). This exchanged token is then injected into the
`Authorization: Bearer` HTTP header and passed to the MCP server.
-## Token exchange with federation
+### Federation flow
When using federated identity providers, ToolHive can map your corporate
identity to an external service identity. This is particularly useful for
accessing cloud services like Google Cloud Platform.
-### Federation flow
-
The client authenticates with your corporate IdP and receives a token:
```json
@@ -268,7 +423,7 @@ Federation-based token exchange has several important characteristics:
## Security and operational benefits
-ToolHive's token exchange approach provides several key advantages:
+ToolHive's backend authentication patterns provide several key advantages:
- **Secure:** MCP servers receive short-lived, properly scoped access tokens
instead of embedding long-lived secrets
@@ -285,26 +440,12 @@ ToolHive's token exchange approach provides several key advantages:
## Choosing the right backend authentication model
-How you configure backend authentication depends on what the MCP server needs to
-call and how that backend service accepts credentials:
-
-- **Static credentials or API keys:** If the MCP server only supports static
- credentials or API keys, configure them in ToolHive directly—either as
- environment variables, secrets, or injected headers. No token exchange or
- embedded authorization server is needed.
-- **Token exchange:** If the MCP server makes authenticated API calls to a
- backend service in the same trust domain as your corporate identity provider
- (for example, an internal API that accepts tokens from your Okta or Entra ID
- tenant), or federation exists between the two, token exchange is a good fit.
- ToolHive exchanges the client's token for a backend-scoped token using RFC
- 8693, preserving the user's identity across services.
-- **Embedded authorization server:** If the MCP server needs to call an external
- API where no federation relationship exists—such as GitHub, Google, or
- Atlassian APIs—the
- [embedded authorization server](./auth-framework.mdx#embedded-authorization-server)
- is a good fit. It runs the full OAuth web flow against the external provider,
- obtaining tokens that the MCP server can use to access those APIs on behalf of
- the user.
+| Scenario | Pattern | Why |
+| ------------------------------------------------------------------- | -------------------------------------------------------------------- | ----------------------------------------------------------------- |
+| Backend only accepts API keys or static credentials | [Static credentials](#static-credentials-and-api-keys) | No OAuth support; configure credentials directly in ToolHive |
+| Backend trusts the same IdP as your clients | [Token exchange (same IdP)](#same-idp-with-token-exchange) | Exchange the client token for a backend-scoped token via RFC 8693 |
+| Backend trusts a federated IdP (for example, Google Cloud) | [Token exchange (federation)](#federated-idps-with-identity-mapping) | Map your corporate identity to the federated service |
+| Backend is an external API with no federation (for example, GitHub) | [Embedded authorization server](#embedded-authorization-server) | Run the full OAuth flow against the external provider |
## Related information
@@ -312,4 +453,9 @@ call and how that backend service accepts credentials:
[Authentication and authorization](./auth-framework.mdx)
- For the embedded authorization server, see
[Embedded authorization server](./auth-framework.mdx#embedded-authorization-server)
+- For configuring the embedded authorization server in Kubernetes, see
+ [Set up embedded authorization server authentication](../guides-k8s/auth-k8s.mdx#set-up-embedded-authorization-server-authentication)
+- For configuring token exchange, see
+ [Configure token exchange](../guides-cli/token-exchange.mdx) (CLI) or
+ [Configure token exchange in Kubernetes](../guides-k8s/token-exchange-k8s.mdx)
- For policy configuration, see [Cedar policies](./cedar-policies.mdx)