diff --git a/docs/toolhive/_partials/_auth-troubleshooting.mdx b/docs/toolhive/_partials/_auth-troubleshooting.mdx new file mode 100644 index 00000000..9a01f5bc --- /dev/null +++ b/docs/toolhive/_partials/_auth-troubleshooting.mdx @@ -0,0 +1,29 @@ +### Authentication issues + +If clients can't authenticate: + +1. Check that the JWT token is valid and not expired +2. Verify that the audience and issuer match your configuration +3. Ensure the JWKS URL is accessible +4. Check the server logs for specific authentication errors: + + ```bash + thv logs + ``` + +### Authorization issues + +If authenticated clients are denied access: + +1. Make sure your Cedar policies explicitly permit the specific action + (remember, default deny) +2. Check that the principal, action, and resource match what's in your policies + (including case and formatting) +3. Examine any conditions in your policies to ensure they're satisfied (for + example, required JWT claims or tool arguments) +4. Remember that Cedar uses a default deny policy—if no policy explicitly + permits an action, it will be denied + +**Troubleshooting tip:** If access is denied, check that your policies +explicitly permit the action. Cedar uses a default deny model—if no policy +matches, the request is denied. diff --git a/docs/toolhive/_partials/_basic-cedar-config.mdx b/docs/toolhive/_partials/_basic-cedar-config.mdx new file mode 100644 index 00000000..b2abc4d8 --- /dev/null +++ b/docs/toolhive/_partials/_basic-cedar-config.mdx @@ -0,0 +1,32 @@ +Create a JSON or YAML file with Cedar policies. Here's an example in JSON +format: + +```json +{ + "version": "1.0", + "type": "cedarv1", + "cedar": { + "policies": [ + // Allow everyone to use the weather tool + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"weather\");", + // Restrict admin_tool to a specific user + "permit(principal == Client::\"alice123\", action == Action::\"call_tool\", resource == Tool::\"admin_tool\");", + // Role-based access: only users with the 'premium' role can call any tool + "permit(principal, action == Action::\"call_tool\", resource) when { principal.claim_roles.contains(\"premium\") };", + // Attribute-based: allow calculator tool only for add/subtract operations + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"calculator\") when { resource.arg_operation == \"add\" || resource.arg_operation == \"subtract\" };" + ], + "entities_json": "[]" + } +} +``` + +You can also define custom resource attributes in `entities_json` for per-tool +ownership or sensitivity labels. + +:::tip + +For more policy examples and advanced usage, see +[Cedar policies](../concepts/cedar-policies.mdx). + +::: diff --git a/docs/toolhive/_partials/_oidc-prerequisites.mdx b/docs/toolhive/_partials/_oidc-prerequisites.mdx new file mode 100644 index 00000000..99e1cb66 --- /dev/null +++ b/docs/toolhive/_partials/_oidc-prerequisites.mdx @@ -0,0 +1,22 @@ +Before you begin, make sure you have: + +- ToolHive installed and working +- Basic familiarity with OAuth, OIDC, and JWT concepts +- An identity provider that supports OpenID Connect (OIDC), such as Google, + GitHub, Microsoft Entra ID (Azure AD), Okta, Auth0, or Kubernetes (for service + accounts) + +From your identity provider, you'll need: + +- Client ID +- Audience value +- Issuer URL +- JWKS URL (for key verification) + +ToolHive uses OIDC to connect to your existing identity provider, so you can +authenticate with your own credentials (for example, Google login) or with +service account tokens (for example, in Kubernetes). ToolHive never sees your +password, only signed tokens from your identity provider. + +For background on authentication, authorization, and Cedar policy examples, see +[Authentication and authorization framework](../concepts/auth-framework.mdx). diff --git a/docs/toolhive/concepts/auth-framework.mdx b/docs/toolhive/concepts/auth-framework.mdx new file mode 100644 index 00000000..ed71d5f7 --- /dev/null +++ b/docs/toolhive/concepts/auth-framework.mdx @@ -0,0 +1,257 @@ +--- +title: Authentication and authorization +description: + Understanding ToolHive's authentication and authorization framework concepts. +--- + +This document explains the concepts behind ToolHive's authentication and +authorization framework, which secures MCP servers by verifying client identity +and controlling access to resources. You'll learn how these systems work +together, why they're designed this way, and the benefits of this approach. + +## Understanding authentication vs. authorization + +When you secure MCP servers, you need to understand the strong separation +between two critical security concepts: + +- **Authentication (authN):** Verifying the identity of clients connecting to + your MCP server ("Who are you?") +- **Authorization (authZ):** Determining what actions authenticated clients are + allowed to perform ("What can you do?") + +You should always perform authentication first, using a trusted identity +provider, and then apply authorization rules to determine what the authenticated +identity can do. ToolHive helps you follow this best practice by acting as a +gateway in front of your MCP servers. This approach lets you use proven identity +systems for authentication, while keeping your authorization policies clear, +flexible, and auditable. You don't need to add custom authentication or +authorization logic to every server—ToolHive handles it for you, consistently +and securely. + +## ToolHive vs. MCP specification + +The official Model Context Protocol (MCP) specification recommends OAuth +2.1-based authorization for HTTP transports, which can require each MCP server +to implement OAuth endpoints and manage tokens. ToolHive takes a different +approach: it centralizes authentication and authorization in its proxy layer, +using OIDC for authentication and Cedar for fine-grained authorization. This +means you don't need to implement OAuth flows or scope management in every +server—just configure ToolHive with your IdP and write clear policies. This +approach is more flexible, secure, and easier to manage for you and your team. + +## Authentication framework + +ToolHive uses OpenID Connect (OIDC), an identity layer built on top of OAuth +2.0, for authentication. OIDC is a widely adopted, interoperable protocol that +lets you connect ToolHive to any OIDC-compliant identity provider (IdP), such as +Google, GitHub, Microsoft Entra ID (Azure AD), Okta, Auth0, or even Kubernetes +service accounts. ToolHive never handles your raw passwords or credentials; +instead, it relies on signed identity tokens (usually JWTs) issued by your +trusted provider. + +### Why use OIDC? + +OIDC provides several key advantages for securing MCP servers: + +- **Standard and interoperable:** You can connect ToolHive to any OIDC-compliant + IdP without custom code, supporting both human users and automated services. +- **Proven and secure:** Authentication is delegated to battle-tested identity + systems, which handle login UI, multi-factor authentication, and password + storage. +- **Decoupled identity management:** You can use your existing SSO/IdP + infrastructure, making onboarding and management seamless. +- **Flexible for users and services:** OIDC supports both interactive user login + (for example, Google sign-in) and service-to-service authentication (for + example, Kubernetes service account tokens). + +### Real-world authentication scenarios + +Understanding how OIDC works in practice helps you design better security for +your MCP servers: + +**User login via Google (OIDC):** You can run an MCP server that requires +authentication using your Google credentials. ToolHive delegates login to +Google, receives a signed ID token, and uses it to authenticate you. This means +users get a familiar login experience while you benefit from Google's security +infrastructure. + +**Service-to-service auth with Kubernetes:** If you run a microservice in a +Kubernetes cluster, it can present its service account token (an OIDC JWT) to +ToolHive. ToolHive validates the token using the cluster's OIDC issuer and JWKS +URL, enabling secure, automated authentication for your internal services. + +### JWT-based authentication + +ToolHive uses JSON Web Tokens (JWTs) for authentication. JWTs are compact, +self-contained tokens that securely transmit identity information. Each JWT has +three parts: + +1. **Header:** Metadata about the token +2. **Payload:** Claims about the entity (typically you or your service) +3. **Signature:** Ensures the token hasn't been altered + +### Authentication flow + +The authentication process follows these steps: + +1. **Token acquisition:** You obtain a JWT from your identity provider. +2. **Token presentation:** You include the JWT in your requests to ToolHive. +3. **Token validation:** ToolHive validates the JWT's signature, expiration, and + claims. +4. **Identity extraction:** ToolHive extracts your identity information from the + validated JWT. + +```mermaid +flowchart TD + Client -->|OIDC Token| ToolHive + ToolHive -->|Validate Token| OIDC_Provider[OIDC Provider] + ToolHive -->|Evaluate Cedar Policy| Cedar_Authorizer[Cedar Authorizer] + Cedar_Authorizer -->|Permit| MCP_Server + Cedar_Authorizer -->|Deny| Denied[403 Forbidden] +``` + +### Identity providers + +ToolHive can integrate with any provider that supports OIDC, including: + +- Google +- GitHub +- Microsoft Entra ID (Azure AD) +- Okta +- Auth0 +- Kubernetes (service account tokens) + +This flexibility lets you use your existing identity infrastructure for both +users and services, reducing operational overhead and improving security. + +## Authorization framework + +After authentication, ToolHive enforces authorization using Amazon's Cedar +policy language. ToolHive acts as a gateway in front of MCP servers, handling +all authorization checks before requests reach the server logic. This means MCP +servers do not need to implement their own OAuth or custom authorization +logic—ToolHive centralizes and standardizes access control. + +### Why Cedar for authorization? + +Cedar provides several advantages for MCP server authorization: + +- **Expressive and flexible:** Cedar supports both role-based (RBAC) and + attribute-based (ABAC) access control patterns, letting you create policies + that match your security requirements. +- **Formally verified:** Cedar's design has been formally verified for safety + and security properties, reducing the risk of policy bugs. +- **Human-readable:** Cedar policies use clear, declarative syntax that's easy + to read, write, and audit. +- **Policy enforcement point:** ToolHive blocks unauthorized requests before + they reach the MCP server, reducing risk and simplifying server code. +- **Secure-by-default:** Authorization is explicit—if a request is not + explicitly permitted, it is denied. Deny rules take precedence over permit + rules (deny overrides). + +### Authorization components + +ToolHive's authorization framework consists of: + +1. **Cedar authorizer:** Evaluates Cedar policies to determine if a request is + authorized +2. **Authorization middleware:** Extracts information from MCP requests and uses + the Cedar Authorizer +3. **Configuration:** A JSON or YAML file that specifies the Cedar policies and + entities + +### Authorization flow + +When a request arrives at an MCP server with authorization enabled: + +1. The JWT middleware authenticates the client and adds JWT claims to the + request context +2. The authorization middleware extracts information from the request + (principal, action, resource, and any arguments) +3. The Cedar authorizer evaluates policies to determine if the request is + authorized +4. If authorized, the request proceeds; otherwise, a 403 Forbidden response is + returned + +```mermaid +flowchart TD + Client -->|JWT| ToolHive + ToolHive -->|Validate JWT| JWT_Middleware + JWT_Middleware -->|Extract Claims| Authz_Middleware + Authz_Middleware -->|Evaluate Cedar Policies| Cedar_Authorizer + Cedar_Authorizer -->|Permit| MCP_Server + Cedar_Authorizer -->|Deny| Denied[403 Forbidden] +``` + +## Security and operational benefits + +ToolHive's authentication and authorization approach provides several key +benefits: + +- **Separation of concerns:** Authentication and authorization are handled + independently, following security best practices. +- **Integration with existing systems:** Use your existing identity + infrastructure (SSO, IdPs, Kubernetes, etc.). +- **Centralized, flexible policy model:** Define precise, auditable access rules + in a single place—no need to modify MCP server code. +- **Secure by default:** Requests are denied unless explicitly permitted by + policy, with deny precedence for maximum safety. +- **Auditable and versionable:** Policies are clear, declarative, and can be + tracked in version control for compliance and review. +- **Developer and operator friendly:** ToolHive acts as a smart proxy, so you + don't need to implement complex OAuth or custom auth logic in every server. + +## Client support for MCP server authentication + +While ToolHive provides a robust authentication and authorization framework for +MCP servers, it's important to understand the current state of client support +across the ecosystem. + +### Current limitations + +Most AI coding clients and MCP client implementations do not currently support +authentication when connecting to MCP servers. This means that many popular AI +development tools expect MCP servers to be accessible without authentication, +which limits the security options available for production deployments. + +### Expected evolution + +As the official MCP specification matures and security becomes a higher priority +for production MCP deployments, we expect to see authentication support +implemented across major AI coding clients. The MCP specification already +includes provisions for OAuth 2.1-based authorization, and client +implementations are likely to adopt these standards over time. + +### Current use cases + +Today, MCP server authentication is primarily valuable for: + +- **Custom AI applications and agent workflows:** If you're building your own AI + application or agent system, you can implement MCP client authentication to + work with ToolHive's secure MCP servers. +- **Kubernetes service account authentication:** For automated services running + in Kubernetes clusters, service account tokens provide a secure way to + authenticate with MCP servers without requiring interactive login flows. +- **Internal tooling and APIs:** Organizations building internal tools that + consume MCP servers can implement authentication to secure access to sensitive + resources and tools. + +### Planning for the future + +When designing your MCP server security strategy, consider that: + +- Authentication support in popular AI coding clients will likely improve over + time +- ToolHive's OIDC-based approach aligns with emerging standards and will be + compatible with future client implementations +- You can start with authenticated MCP servers for internal use cases and + gradually expand as client support improves + +This evolving landscape means that while authentication capabilities exist +today, their practical application depends on your specific use case and client +requirements. + +## Related information + +- For detailed policy writing guidance, see + [Cedar policies](./cedar-policies.mdx) diff --git a/docs/toolhive/concepts/cedar-policies.mdx b/docs/toolhive/concepts/cedar-policies.mdx new file mode 100644 index 00000000..fca7eeb7 --- /dev/null +++ b/docs/toolhive/concepts/cedar-policies.mdx @@ -0,0 +1,408 @@ +--- +title: Cedar policies +description: + Writing and configuring Cedar policies for MCP server authorization. +--- + +This document provides detailed guidance on writing and configuring Cedar +policies for MCP server authorization. You'll learn how to create effective +policies, configure authorization settings, and troubleshoot common issues. + +:::info + +For the conceptual overview of authentication and authorization, see +[Authentication and authorization framework](./auth-framework.mdx). + +::: + +## Cedar policy language + +Cedar policies express authorization rules in a clear, declarative syntax: + +```text +permit|forbid(principal, action, resource) when { conditions }; +``` + +- `permit` or `forbid`: Whether to allow or deny the operation +- `principal`: The entity making the request (the client) +- `action`: The operation being performed +- `resource`: The object being accessed +- `conditions`: Optional conditions that must be satisfied + +## MCP-specific entities + +In the context of MCP servers, Cedar policies use the following entities: + +### Principal + +The client making the request, identified by the `sub` claim in the JWT token: + +- Format: `Client::` +- Example: `Client::user123` + +### Action + +The operation being performed on an MCP feature: + +- Format: `Action::` +- Examples: + - `Action::"call_tool"`: Call a tool + - `Action::"get_prompt"`: Get a prompt + - `Action::"read_resource"`: Read a resource + - `Action::"list_tools"`: List available tools + +### Resource + +The object being accessed: + +- Format: `::` +- Examples: + - `Tool::"weather"`: The weather tool + - `Prompt::"greeting"`: The greeting prompt + - `Resource::"data"`: The data resource + +## Configuration formats + +You can configure Cedar authorization using either JSON or YAML format: + +### JSON configuration + +```json +{ + "version": "1.0", + "type": "cedarv1", + "cedar": { + "policies": [ + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"weather\");", + "permit(principal, action == Action::\"get_prompt\", resource == Prompt::\"greeting\");", + "permit(principal, action == Action::\"read_resource\", resource == Resource::\"data\");" + ], + "entities_json": "[]" + } +} +``` + +### YAML configuration + +```yaml +version: '1.0' +type: cedarv1 +cedar: + policies: + - 'permit(principal, action == Action::"call_tool", resource == + Tool::"weather");' + - 'permit(principal, action == Action::"get_prompt", resource == + Prompt::"greeting");' + - 'permit(principal, action == Action::"read_resource", resource == + Resource::"data");' + entities_json: '[]' +``` + +### Configuration fields + +- `version`: The version of the configuration format +- `type`: The type of authorization configuration (currently only `cedarv1` is + supported) +- `cedar`: The Cedar-specific configuration + - `policies`: An array of Cedar policy strings + - `entities_json`: A JSON string representing Cedar entities + +## Writing effective policies + +Understanding how to write Cedar policies is crucial for securing your MCP +servers effectively. This section provides practical guidance for creating +policies that match your security requirements. + +### Basic policy patterns + +Start with simple policies and build complexity as needed: + +#### Allow specific tool access + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"weather"); +``` + +This policy allows any authenticated client to call the weather tool. It's +useful when you want to provide broad access to specific functionality. + +#### Allow specific user access + +```text +permit(principal == Client::"user123", action == Action::"call_tool", resource); +``` + +This policy allows a specific user to call any tool. Use this pattern when you +need to grant broad permissions to trusted users. + +### Role-based access control (RBAC) + +RBAC policies use roles from JWT claims to determine access: + +```text +permit(principal, action == Action::"call_tool", resource) when { + principal.claim_roles.contains("admin") +}; +``` + +This policy allows clients with the "admin" role to call any tool. RBAC is +effective when you have well-defined roles in your organization. + +### Attribute-based access control (ABAC) + +ABAC policies use multiple attributes to make fine-grained decisions: + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"sensitive_data") when { + principal.claim_roles.contains("data_analyst") && + resource.arg_data_level <= principal.claim_clearance_level +}; +``` + +This policy allows data analysts to access sensitive data, but only if their +clearance level is sufficient. ABAC provides the most flexibility for complex +security requirements. + +## Working with JWT claims + +JWT claims from your identity provider become available in policies with a +`claim_` prefix. You can use these claims in two ways: + +**On the principal entity:** + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"weather") when { + principal.claim_name == "John Doe" +}; +``` + +**In the context:** + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"weather") when { + context.claim_name == "John Doe" +}; +``` + +Both approaches work identically. Choose the one that makes your policies more +readable. + +## Working with tool arguments + +Tool arguments become available in policies with an `arg_` prefix. This lets you +create policies based on the specific parameters of requests: + +**On the resource entity:** + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"weather") when { + resource.arg_location == "New York" || resource.arg_location == "London" +}; +``` + +**In the context:** + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"weather") when { + context.arg_location == "New York" || context.arg_location == "London" +}; +``` + +This policy allows weather tool calls only for specific locations, demonstrating +how you can control access based on request parameters. + +## List operations and filtering + +List operations (`tools/list`, `prompts/list`, `resources/list`) work +differently from other operations. They're always allowed, but the response is +automatically filtered based on what the user can actually access: + +- `tools/list` shows only tools the user can call (based on `call_tool` + policies) +- `prompts/list` shows only prompts the user can get (based on `get_prompt` + policies) +- `resources/list` shows only resources the user can read (based on + `read_resource` policies) + +You don't need to write explicit policies for list operations. Instead, focus on +the underlying access policies, and the lists will be filtered automatically. + +For example, if you have this policy: + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"weather"); +``` + +Then `tools/list` will only show the "weather" tool for that user. + +## Policy evaluation and secure defaults + +Understanding how Cedar evaluates policies helps you write more effective and +secure authorization rules. + +### Evaluation order + +ToolHive's policy evaluation follows a secure-by-default, least-privilege model: + +1. **Deny precedence:** If any `forbid` policy matches, the request is denied +2. **Permit evaluation:** If any `permit` policy matches, the request is + authorized +3. **Default deny:** If no policy matches, the request is denied + +This means that `forbid` policies always override `permit` policies, and any +request not explicitly permitted is denied. This approach minimizes risk and +ensures that only authorized actions are allowed. + +### Designing secure policies + +When writing policies, follow these principles: + +**Start with least privilege:** Begin by denying everything, then add specific +permissions as needed. This approach is more secure than starting with broad +permissions and trying to restrict them. + +**Use explicit deny sparingly:** While `forbid` policies can be useful, they can +also make your policy set harder to understand. In most cases, the default deny +behavior is sufficient. + +**Test your policies:** Always test policies with real requests to ensure they +work as expected. Pay special attention to edge cases and error conditions. + +## Advanced policy examples + +### Combining JWT claims and tool arguments + +You can combine JWT claims and tool arguments in your policies to create more +sophisticated authorization rules: + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"sensitive_data") when { + principal.claim_roles.contains("data_analyst") && + resource.arg_data_level <= principal.claim_clearance_level +}; +``` + +This policy allows clients with the "data_analyst" role to access the +sensitive_data tool, but only if their clearance level (from JWT claims) is +sufficient for the requested data level (from tool arguments). + +### Multi-tenant environments + +In multi-tenant environments, you can use policies to isolate tenants: + +```text +permit(principal, action, resource) when { + principal.claim_tenant_id == resource.tenant_id +}; +``` + +This ensures that clients can only access resources belonging to their tenant. + +### Data sensitivity levels + +For data with different sensitivity levels: + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"data_access") when { + principal.claim_clearance_level >= resource.arg_data_sensitivity +}; +``` + +This ensures that clients can only access data within their clearance level. + +### Geographic restrictions + +For geographically restricted resources: + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"geo_restricted") when { + principal.claim_location in ["US", "Canada", "Mexico"] +}; +``` + +This restricts access based on the client's location. + +### Time-based access + +For resources that should only be accessible during certain hours: + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"business_hours") when { + context.current_hour >= 9 && context.current_hour <= 17 +}; +``` + +This restricts access to business hours only. + +## Entity attributes + +Cedar entities can have attributes that can be used in policy conditions. The +authorization middleware automatically adds JWT claims and tool arguments as +attributes to the principal entity. + +You can also define custom entities with attributes in the `entities_json` field +of the configuration file: + +```json +{ + "version": "1.0", + "type": "cedarv1", + "cedar": { + "policies": [ + "permit(principal, action == Action::\"call_tool\", resource) when { resource.owner == principal.claim_sub };" + ], + "entities_json": "[ + { + \"uid\": \"Tool::weather\", + \"attrs\": { + \"owner\": \"user123\" + } + } + ]" + } +} +``` + +This configuration defines a custom entity for the weather tool with an `owner` +attribute set to `user123`. The policy allows clients to call tools only if they +own them. + +## Troubleshooting policies + +When policies don't work as expected, follow this systematic approach: + +### Request is denied unexpectedly + +1. **Check policy syntax:** Ensure your policies are correctly formatted and use + valid Cedar syntax. +2. **Verify entity matching:** Confirm that the principal, action, and resource + in your policies match the actual values in the request. +3. **Test conditions:** Check that any conditions in your policies are satisfied + by the request context. +4. **Remember default deny:** If no policy explicitly permits the request, it + will be denied. + +### JWT claims are not available + +1. **Verify JWT middleware:** Ensure that JWT authentication is configured + correctly and running before authorization. +2. **Check token claims:** Verify that the JWT token contains the expected + claims. +3. **Use correct prefix:** Remember that JWT claims are available with a + `claim_` prefix. + +### Tool arguments are not available + +1. **Check request format:** Ensure that tool arguments are correctly specified + in the request. +2. **Use correct prefix:** Remember that tool arguments are available with an + `arg_` prefix. +3. **Verify argument names:** Confirm that the argument names in your policies + match those in the actual requests. + +## Related information + +- For the conceptual overview, see + [Authentication and authorization framework](./auth-framework.mdx) +- For detailed Cedar policy syntax, see + [Cedar documentation](https://docs.cedarpolicy.com/) diff --git a/docs/toolhive/guides-cli/auth.mdx b/docs/toolhive/guides-cli/auth.mdx new file mode 100644 index 00000000..1c6a0e19 --- /dev/null +++ b/docs/toolhive/guides-cli/auth.mdx @@ -0,0 +1,164 @@ +--- +title: Authentication and authorization +description: + How to set up authentication and authorization for MCP servers using the + ToolHive CLI. +--- + +import OidcPrerequisites from '../_partials/_oidc-prerequisites.mdx'; +import BasicCedarConfig from '../_partials/_basic-cedar-config.mdx'; +import AuthTroubleshooting from '../_partials/_auth-troubleshooting.mdx'; + +This guide shows you how to secure your MCP servers using OAuth-based +authentication and Cedar-based authorization policies with the ToolHive CLI. + +:::info + +Authentication and authorization are emerging capabilities in the MCP ecosystem. +The official MCP authorization specification is still evolving, and client +support for these features is limited. ToolHive is leading the way in +implementing these capabilities, but you may encounter some limitations with +certain clients. + +::: + +## Prerequisites + + + +## Set up authentication + +### Step 1: Gather OIDC configuration + +First, collect the necessary information from your identity provider: + +- Client ID +- Audience value +- Issuer URL +- JWKS URL (for key verification) + +### Step 2: Run an MCP server with authentication + +Use the following command to start an MCP server with authentication enabled: + +```bash +thv run \ + --oidc-audience \ + --oidc-client-id \ + --oidc-issuer \ + --oidc-jwks-url \ + +``` + +Replace the placeholders with your actual OIDC configuration. + +### Step 3: Test authentication + +Once your server is running with authentication enabled, clients must include a +valid JWT (JSON Web Token) in the `Authorization` header of each HTTP request. +The token should: + +- Be issued by your configured identity provider +- Include the correct audience claim +- Not be expired +- Have a valid signature + +:::note[Client support limitations] + +Client support for authentication is limited at this time. While some clients +support HTTP headers with SSE MCP client configurations, we do not recommend +passing JWT tokens in this way since it requires manual configuration and +exposes your token in plain text. + +We are working on a solution within ToolHive to securely handle authentication +for clients. Stay tuned for updates on this feature. + +::: + +:::note[Obtaining JWT tokens] + +How to obtain JWT tokens varies by identity provider and is outside the scope of +this guide. Consult your identity provider's documentation for specific +instructions on: + +- Interactive user authentication flows (OAuth 2.0 Authorization Code flow) +- Service-to-service authentication (Client Credentials flow) +- API token generation and management + +For Kubernetes service accounts, tokens are automatically mounted at +`/var/run/secrets/kubernetes.io/serviceaccount/token` in pods. + +::: + +To verify that authentication is working, you can use a tool like `curl` to make +a request to your MCP server: + +```bash +curl -H "Authorization: Bearer " \ + +``` + +## Set up authorization + +ToolHive uses Amazon's Cedar policy language for fine-grained, secure-by-default +authorization. Authorization is explicit: if a request is not explicitly +permitted by a policy, it is denied. Deny rules always take precedence over +permit rules. + +### Step 1: Create an authorization configuration file + + + +Save this file to a location accessible to ToolHive, such as +`/path/to/authz-config.json`. + +### Step 2: Run an MCP server with authorization + +Start your MCP server with the authorization configuration: + +```bash +thv run \ + --authz-config /path/to/authz-config.json \ + +``` + +You can combine this with the authentication parameters from the previous +section: + +```bash +thv run \ + --oidc-audience \ + --oidc-client-id \ + --oidc-issuer \ + --oidc-jwks-url \ + --authz-config /path/to/authz-config.json \ + +``` + +### Step 3: Test authorization + +Once your server is running with authorization enabled, clients will be subject +to the Cedar policies defined in your configuration file. When a client attempts +to perform an action, ToolHive will evaluate the request against the policies. +If the request is permitted, the action will proceed; otherwise, it will be +denied with a 403 Forbidden response. + +## Troubleshooting + + + +### CLI-specific issues + +If you're having issues with the CLI: + +1. Verify that the configuration file path is correct and accessible +2. Check that the server name matches an available MCP server +3. Ensure all required CLI flags are provided + +## Related information + +- For conceptual understanding, see + [Authentication and authorization framework](../concepts/auth-framework.mdx) +- For detailed Cedar policy syntax, see + [Cedar policies](../concepts/cedar-policies.mdx) and the + [Cedar documentation](https://docs.cedarpolicy.com/) diff --git a/docs/toolhive/guides-k8s/auth-k8s.mdx b/docs/toolhive/guides-k8s/auth-k8s.mdx new file mode 100644 index 00000000..f916b585 --- /dev/null +++ b/docs/toolhive/guides-k8s/auth-k8s.mdx @@ -0,0 +1,380 @@ +--- +title: Authentication and authorization +description: + How to set up authentication and authorization for MCP servers in Kubernetes + using the ToolHive Operator. +--- + +import OidcPrerequisites from '../_partials/_oidc-prerequisites.mdx'; +import BasicCedarConfig from '../_partials/_basic-cedar-config.mdx'; +import AuthTroubleshooting from '../_partials/_auth-troubleshooting.mdx'; + +This guide shows you how to secure your MCP servers in Kubernetes using +authentication and authorization with the ToolHive Operator. + +:::info + +Authentication and authorization are emerging capabilities in the MCP ecosystem. +The official MCP authorization specification is still evolving, and client +support for these features is limited. ToolHive is leading the way in +implementing these capabilities, but you may encounter some limitations with +certain clients. + +::: + +## Prerequisites + +You'll need: + +- Kubernetes cluster with RBAC enabled +- ToolHive Operator installed (see + [Deploy the ToolHive Operator with Helm](./deploy-operator-helm.mdx)) +- `kubectl` access to your cluster + +## Choose your authentication approach + +There are two main ways to authenticate with MCP servers running in Kubernetes: + +### Approach 1: External identity provider authentication + +Use this when you want to authenticate users or external services using +providers like Google, GitHub, Microsoft Entra ID, Okta, or Auth0. + +**Prerequisites for external IdP:** + + + +### Approach 2: Kubernetes service-to-service authentication + +Use this when you have client applications running in the same Kubernetes +cluster that need to call MCP servers. This approach uses Kubernetes service +account tokens for authentication. + +**Prerequisites for service-to-service:** + +- Client applications running in Kubernetes pods +- Understanding of Kubernetes service accounts and RBAC + +## Set up external identity provider authentication + +### Step 1: Create an MCPServer with external OIDC + +Create an `MCPServer` resource configured to accept tokens from your external +identity provider. The ToolHive proxy will handle authentication before +forwarding requests to the MCP server. + +```yaml title="mcp-server-external-auth.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server-external + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: sse + port: 8080 + permissionProfile: + type: builtin + name: network + # Authentication configuration for external IdP + auth: + oidc: + audience: '' + clientId: '' + issuer: '' + jwksUrl: '' + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' +``` + +Replace the OIDC placeholders with your actual identity provider configuration. + +### Step 2: Apply the MCPServer resource + +```bash +kubectl apply -f mcp-server-external-auth.yaml +``` + +### Step 3: Test external authentication + +Clients connecting to this MCP server must include a valid JWT token from your +configured identity provider in their requests. The ToolHive proxy will validate +the token before allowing access to the MCP server. + +:::note Obtaining JWT tokens + +How to obtain JWT tokens varies by identity provider and is outside the scope of +this guide. Consult your identity provider's documentation for specific +instructions on: + +- Interactive user authentication flows (OAuth 2.0 Authorization Code flow) +- Service-to-service authentication (Client Credentials flow) +- API token generation and management + +For Kubernetes service accounts, tokens are automatically mounted at +`/var/run/secrets/kubernetes.io/serviceaccount/token` in pods. + +::: + +## Set up Kubernetes service-to-service authentication + +This approach is ideal when you have client applications running in the same +Kubernetes cluster that need to call MCP servers. + +### Step 1: Create service account for client application + +Create a service account that your client application will use: + +```yaml title="client-service-account.yaml" +apiVersion: v1 +kind: ServiceAccount +metadata: + name: mcp-client + namespace: client-apps +``` + +```bash +kubectl apply -f client-service-account.yaml +``` + +### Step 2: Create MCPServer for service-to-service auth + +Create an `MCPServer` resource configured to accept Kubernetes service account +tokens: + +```yaml title="mcp-server-k8s-auth.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server-k8s + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: sse + port: 8080 + permissionProfile: + type: builtin + name: network + # Authentication configuration for Kubernetes service accounts + auth: + oidc: + audience: 'toolhive' + clientId: 'mcp-client.client-apps.svc.cluster.local' + issuer: 'https://kubernetes.default.svc' + jwksUrl: 'https://kubernetes.default.svc/openid/v1/jwks' + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' +``` + +This configuration only allows requests from pods using the `mcp-client` service +account in the `client-apps` namespace. + +```bash +kubectl apply -f mcp-server-k8s-auth.yaml +``` + +### Step 3: Deploy client application with service account + +Deploy your client application using the service account: + +```yaml title="client-app.yaml" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mcp-client-app + namespace: client-apps +spec: + replicas: 1 + selector: + matchLabels: + app: mcp-client-app + template: + metadata: + labels: + app: mcp-client-app + spec: + serviceAccountName: mcp-client + containers: + - name: client + image: your-client-app:latest + env: + - name: MCP_SERVER_URL + value: 'http://weather-server-k8s.toolhive-system.svc.cluster.local:8080' +``` + +```bash +kubectl apply -f client-app.yaml +``` + +Your client application can now authenticate to the MCP server using its +Kubernetes service account token, which is automatically mounted at +`/var/run/secrets/kubernetes.io/serviceaccount/token`. + +## Set up authorization + +Both authentication approaches can use the same authorization configuration +using Cedar policies. + +### Step 1: Create authorization configuration + + + +### Step 2: Create a ConfigMap with policies + +Store your authorization configuration in a ConfigMap: + +```yaml title="authz-configmap.yaml" +apiVersion: v1 +kind: ConfigMap +metadata: + name: authz-config + namespace: toolhive-system +data: + authz-config.json: | + { + "version": "1.0", + "type": "cedarv1", + "cedar": { + "policies": [ + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"weather\");", + "permit(principal == Client::\"alice123\", action == Action::\"call_tool\", resource == Tool::\"admin_tool\");", + "permit(principal, action == Action::\"call_tool\", resource) when { principal.claim_roles.contains(\"premium\") };" + ], + "entities_json": "[]" + } + } +``` + +```bash +kubectl apply -f authz-configmap.yaml +``` + +### Step 3: Update MCPServer to use authorization + +Add the authorization configuration to your `MCPServer` resources: + +```yaml title="mcp-server-with-authz.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server-with-authz + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: sse + port: 8080 + permissionProfile: + type: builtin + name: network + # Authentication configuration + auth: + oidc: + audience: 'toolhive' + clientId: 'mcp-client.client-apps.svc.cluster.local' + issuer: 'https://kubernetes.default.svc' + jwksUrl: 'https://kubernetes.default.svc/openid/v1/jwks' + # Authorization configuration + authorization: + configMapName: authz-config + configMapKey: authz-config.json + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' +``` + +```bash +kubectl apply -f mcp-server-with-authz.yaml +``` + +## Test your setup + +### Test external IdP authentication + +1. Deploy the external IdP configuration +2. Obtain a valid JWT token from your identity provider +3. Make a request to the MCP server including the token + +### Test service-to-service authentication + +1. Deploy both the MCP server and client application +2. Check that the client can successfully call the MCP server +3. Verify authentication in the ToolHive proxy logs: + +```bash +kubectl logs -n toolhive-system -l app.kubernetes.io/name=weather-server-k8s +``` + +### Test authorization + +1. Make requests that should be permitted by your policies +2. Make requests that should be denied +3. Check the proxy logs to see authorization decisions + +## Troubleshooting + + + +### Kubernetes-specific issues + +**MCPServer resource issues:** + +- Check the MCPServer status: `kubectl get mcpserver -n toolhive-system` +- Describe the resource for details: + `kubectl describe mcpserver weather-server-k8s -n toolhive-system` + +**Service account issues:** + +- Verify the service account exists: `kubectl get sa -n client-apps mcp-client` +- Check RBAC permissions if needed + +**ConfigMap mounting issues:** + +- Verify the ConfigMap exists: + `kubectl get configmap -n toolhive-system authz-config` +- Check the ConfigMap content: + `kubectl get configmap authz-config -n toolhive-system -o yaml` + +**OIDC configuration issues:** + +- For external IdP: Ensure the issuer URL is accessible from within the cluster +- For Kubernetes auth: Ensure the Kubernetes API server has OIDC enabled +- Check that the JWKS URL returns valid keys + +**Network connectivity:** + +- Verify pods can reach the Kubernetes API server +- Check cluster DNS resolution +- Test service-to-service connectivity: + `kubectl exec -n client-apps deployment/mcp-client-app -- curl http://weather-server-k8s.toolhive-system.svc.cluster.local:8080` + +**ToolHive Operator issues:** + +- Check operator logs: + `kubectl logs -n toolhive-system -l app.kubernetes.io/name=toolhive-operator` +- Verify the operator is running: `kubectl get pods -n toolhive-system` + +## Related information + +- For conceptual understanding, see + [Authentication and authorization framework](../concepts/auth-framework.mdx) +- For detailed Cedar policy syntax, see + [Cedar policies](../concepts/cedar-policies.mdx) and the + [Cedar documentation](https://docs.cedarpolicy.com/) +- For running MCP servers without authentication, see + [Run MCP servers in Kubernetes](./run-mcp-k8s.mdx) +- For ToolHive Operator installation, see + [Deploy the ToolHive Operator with Helm](./deploy-operator-helm.mdx) diff --git a/sidebars.ts b/sidebars.ts index 7cd3c87b..d997b46e 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -114,6 +114,7 @@ const sidebars: SidebarsConfig = { ], }, 'toolhive/guides-cli/telemetry-and-metrics', + 'toolhive/guides-cli/auth', 'toolhive/guides-cli/build-containers', 'toolhive/guides-cli/advanced-cicd', { @@ -151,6 +152,7 @@ const sidebars: SidebarsConfig = { 'toolhive/guides-k8s/customize-tools', 'toolhive/guides-k8s/telemetry-and-metrics', 'toolhive/guides-k8s/logging', + 'toolhive/guides-k8s/auth-k8s', 'toolhive/reference/crd-spec', ], }, @@ -169,6 +171,8 @@ const sidebars: SidebarsConfig = { 'toolhive/concepts/mcp-primer', 'toolhive/concepts/registry-criteria', 'toolhive/concepts/observability', + 'toolhive/concepts/auth-framework', + 'toolhive/concepts/cedar-policies', ], },