Introduction to enterprise authorization
Enterprise authorization is a capability of Stacklok Enterprise. For a full comparison of ToolHive Community and Stacklok Enterprise capabilities, see Stacklok Enterprise.
Enterprise authorization represents MCP access as declarative Kubernetes custom resources. Platform teams define reusable roles, and platform or namespace admins bind those roles to identity-provider groups and MCP servers. The operator compiles the resources into the Cedar policies ToolHive enforces at runtime.
The model
Enterprise authorization uses four custom resources modeled on Kubernetes role-based access control (RBAC).
ClusterPlatformRole(reference) defines what a role can do. It is product-agnostic. Per-product action vocabularies live underspec.productActions[], keyed by API group. The only supported product API group istoolhive.enterprise.stacklok.com. Wildcard"*"is allowed, which is how the built-inwriterrole grants every action.ClusterPlatformRoleBinding(reference) defines who gets a role, cluster-wide. It maps identity-provider groups and roles (as carried in the JWT) to aClusterPlatformRole.PlatformRoleBinding(reference) is the namespaced sibling ofClusterPlatformRoleBinding. It lets a namespace owner author grants for resources in their own namespace without involving the cluster admin. Cluster-scoped and namespace-scoped bindings combine with union semantics: a subject is granted a role if any matching binding says so.ToolhiveAuthorizationPolicy(reference) defines where access applies. It attaches one or more role bindings to a specific MCP target: anMCPServerorMCPRemoteProxy. It also supports optionalruleRestrictions(narrow the bound role to a specific tool, prompt, or resource), atoolHintFilter(gate by thereadOnlyHintordestructiveHintannotations the server advertises), and adeny[]list that compiles to a Cedarforbidrule and overrides every grant. MultipleToolhiveAuthorizationPolicyobjects can attach to the same target; their effective principal sets and deny lists are unioned.
Claim mapping
Bindings refer to identity-provider groups and roles. Those values come from
claims in the JWT that ToolHive validates on every request. By default, the
platform derives the claim names from the IdP type it detects, so a cluster
admin typically does not need to configure them. For Microsoft Entra ID, the
defaults are the roles claim (the app-role claim) and the groups claim.
To override the defaults, set the groupsClaim and rolesClaim keys on the
platform identity-provider ConfigMap. The enterprise installer creates this
ConfigMap with a release-specific name in the platform namespace, for example
stacklok-enterprise-platform-enterprise-manager-idp-config. Find it with
kubectl get configmap -A | grep idp-config, then edit the keys to match the
claims your IdP emits. For the identity-provider side of this setup, where you
configure the IdP to emit those group and role claims, see
Configure platform identity.
Inside each binding, the from[] field is a list of PrincipalCondition
objects. Each condition has two arrays: groups[] and roles[]. The two arrays
inside one condition are AND-ed: the subject must satisfy both the groups list
and the roles list to match. Across multiple conditions in the same from[],
the conditions are OR-ed: a subject matches the binding if any one of them
matches. At least one of groups[] or roles[] must be non-empty in every
condition, which the CRD enforces with a CEL validation rule.
For example, this binding matches users who are in the mcp-engineers group and
hold the mcp-viewer role, or anyone who holds the mcp-admin role:
from:
- groups: [mcp-engineers]
roles: [mcp-viewer]
- roles: [mcp-admin]
For the broader picture of how ToolHive verifies tokens and integrates with an OIDC provider, see Authentication and authorization.
Built-in roles
The operator includes two ClusterPlatformRole objects.
readergrants the MCP read operations andcall_tool. Thecall_toolactions can be narrowed at bind time withtoolHintFilter.readOnlyHint: true, which restricts the binding to tools the MCP server itself annotates as read-only.writergrants every action via wildcard.
You can list them with kubectl get clusterplatformroles (or the short form,
kubectl get cpr). Build a custom ClusterPlatformRole only when you need a
narrower action vocabulary than reader and writer offer.
Next steps
- Quickstart - GitHub MCP with Entra ID for a hands-on walkthrough against a real identity provider
- Namespace self-service authorization to see how a namespace owner authors their own bindings
- ToolhiveAuthorizationPolicy for the full schema of the attachment object that ties everything together
- Cedar policies and the authorization policy reference for the underlying policy language and the full vocabulary the operator emits