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)