Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 36 additions & 7 deletions docs/toolhive/concepts/cedar-policies.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -567,20 +567,49 @@ groups or roles using the same `principal in THVGroup::"..."` syntax.
### How it works

1. The embedded authorization server authenticates the user with your upstream
identity provider and issues a ToolHive JWT.
2. The Cedar authorizer reads claims from the upstream token (not just the
ToolHive-issued JWT).
3. Group claims are extracted and used to build `THVGroup` parent entities for
the principal.
identity provider, stores the upstream access token, and issues a ToolHive
JWT for the client.
2. The authorizer resolves which token's claims to evaluate:
- When `primaryUpstreamProvider` is set, it reads claims from the named
upstream's stored access token.
- When `primaryUpstreamProvider` is unset and an embedded auth server is
configured, it reads claims from the first upstream provider declared on
the auth server.
- When `primaryUpstreamProvider` is unset and no embedded auth server is
configured, it reads claims from the bearer token on the original client
request.
3. Group and role claims are extracted from the resolved claim source and used
to build `THVGroup` parent entities for the principal.
4. Policies using `principal in THVGroup::"<group>"` evaluate correctly.

:::note

If the upstream token is opaque (not a JWT), the authorizer denies the request.
There is no silent fallback to ToolHive-issued claims only.
If the resolved upstream token is opaque (not a JWT), the authorizer denies the
request. There is no silent fallback to the client request's claims.

:::

### How the upstream provider is chosen

In Kubernetes, how you configure `primaryUpstreamProvider` depends on the
resource type:

- **VirtualMCPServer:** the embedded auth server can declare multiple upstream
providers. Set `incomingAuth.authzConfig.inline.primaryUpstreamProvider` on
the `VirtualMCPServer` to choose which upstream's access token Cedar
evaluates. When the field is empty, the operator defaults to the first entry
in `authServerConfig.upstreamProviders` and emits an
`AuthzUpstreamSelectionWarning` status condition naming the chosen provider.
See
[Cedar authorization claim source](../guides-vmcp/authentication.mdx#cedar-authorization-claim-source)
in the vMCP authentication guide for the override syntax and validation
behavior.
- **MCPServer and MCPRemoteProxy:** the inline `primaryUpstreamProvider` field
is accepted but has no effect on these resources. Setting it surfaces an
`AuthzPrimaryUpstreamProviderIgnored` advisory status condition. The embedded
auth server for these resources runs through a referenced
`MCPExternalAuthConfig` and supports a single upstream provider.

## Policy evaluation and secure defaults

Understanding how Cedar evaluates policies helps you write more effective and
Expand Down
13 changes: 13 additions & 0 deletions docs/toolhive/guides-k8s/auth-k8s.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,19 @@ membership. See
[Upstream identity provider claims](../concepts/cedar-policies.mdx#upstream-identity-provider-claims)
for details.

:::note[`primaryUpstreamProvider` on MCPServer and MCPRemoteProxy]

The embedded authorization server for `MCPServer` and `MCPRemoteProxy` resources
is configured through a referenced `MCPExternalAuthConfig` and supports a single
upstream provider, so there is no provider selection to make. The
`primaryUpstreamProvider` field on the inline authz config is accepted on these
resources but has no effect; setting it surfaces an
`AuthzPrimaryUpstreamProviderIgnored` advisory status condition. Use a
[`VirtualMCPServer`](../guides-vmcp/authentication.mdx#cedar-authorization-claim-source)
when you need Cedar to choose between multiple upstream providers.

:::

**Step 1: Create authorization configuration**

<BasicCedarConfig />
Expand Down
60 changes: 60 additions & 0 deletions docs/toolhive/guides-vmcp/authentication.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,66 @@ spec:
# highlight-end
```

### Cedar authorization claim source

When you configure Cedar policies under `incomingAuth.authzConfig.inline`, the
operator binds Cedar's claim source to one of the providers in
`authServerConfig.upstreamProviders` so that group and role policies evaluate
against upstream IDP claims rather than the ToolHive-issued JWT.

By default, the operator selects the first entry in
`authServerConfig.upstreamProviders`. With two or more upstreams declared the
choice is ambiguous, so the operator additionally emits an
`AuthzUpstreamSelectionWarning` status condition naming the chosen provider so
you can verify the default matches your intent.

To pin Cedar to a specific upstream, set `primaryUpstreamProvider` on the inline
authz config:

```yaml title="VirtualMCPServer resource"
spec:
incomingAuth:
type: oidc
oidcConfigRef:
name: my-oidc-config
audience: https://mcp.example.com/mcp
authzConfig:
type: inline
inline:
# highlight-next-line
primaryUpstreamProvider: github
policies:
- 'permit(principal in THVGroup::"engineering", action, resource);'
entitiesJson: '[]'
authServerConfig:
issuer: https://auth.example.com
upstreamProviders:
- name: github
type: oauth2
oauth2Config: { ... }
- name: google
type: oidc
oidcConfig: { ... }
```

The value must match an entry in `authServerConfig.upstreamProviders`. Setting
the field on a single-upstream config is allowed but redundant: the default
already resolves to that upstream.

Rejection behavior at admission:

- **Name does not match a declared upstream:** the `VirtualMCPServer` is
rejected with `AuthServerConfigValidated=False` and `AuthzUpstreamUnknown`.
Cedar would otherwise deny every request at runtime, so the operator rejects
at admission instead.
- **Field set without an embedded auth server:** the `VirtualMCPServer` is
rejected with `AuthzPrimaryProviderRequiresAuthServer`. Either remove the
field or configure `authServerConfig`.

For background on how Cedar resolves claims from the upstream token versus the
ToolHive-issued JWT, see
[Upstream identity provider claims](../concepts/cedar-policies.mdx#upstream-identity-provider-claims).

### Session storage

By default, upstream tokens are stored in memory and lost on pod restart. For
Expand Down