Files
Enterprise-MCP-Service-Bus/RI/docs/interfaces/identity-oidc.en-us.md
2026-09-14 14:01:27 -03:00

3.2 KiB

Interface: MCP Client identity and registration (OIDC)

Contract for registering an MCP Client and the OIDC token it presents to the Gateway. Implementation: src/emcp_bus/identity/models.py, identity/authn.py, identity/shared_client_lint.py. See AS-BUILT.md.

Registering an MCP Client

One YAML file per client in config/clients/*.yaml, validated against ClientRegistration:

client_id: finance-payments-agent   # exactly the token's azp/client_id claim
profile: Finance.Payments           # name of a ClientProfile under config/clients/profiles/
environment: production             # "dev" | "ci" | "production"
description: "Finance agent authorized to create/execute payments (R3, approval above threshold)"

(real example: config/clients/finance-payments-agent.yaml)

client_id is the sole link between an authenticated identity and a ClientProfile — two distinct client_ids never share a profile unless that is an explicit and auditable decision (the shared-client rule, identity/shared_client_lint.py, wired into make validate-schemas).

Registering the client with the IdP

The client must exist in the IdP realm with the client_credentials grant enabled and an audience mapper pointing at the Gateway's OIDC_AUDIENCE (emcp-gateway by default). See deploy/keycloak/realm-export.json for the 3 example clients already configured this way (sales-read-agent, sales-write-agent, finance-payments-agent).

Access token contract

Any OIDC-compliant IdP works (TokenValidator is provider-agnostic). Claims required and validated by TokenValidator.validate:

Claim Use
exp Expiration — required, validated with leeway_seconds=5.
iss Must match OIDC_ISSUER exactly.
aud Must contain OIDC_AUDIENCE.
azp (preferred) or client_id Sole source of client identity — never clientInfo from the MCP request nor any body field.

Signature verified via JWKS (OIDC_JWKS_URL), RS256 algorithm. Failure of any check raises AuthenticationError, treated by the Gateway as UNAUTHENTICATED (fail-closed).

How a new client registers, end to end

  1. Create/update the client in the IdP (client_credentials grant, audience mapper).
  2. Create config/clients/<client-id>.yaml (ClientRegistration).
  3. Point profile at an existing ClientProfile, or create a new one under config/clients/profiles/ (see registry-capability-manifest.md for how a capability references a profile via entitlements).
  4. make validate-schemas.
  5. Restart the Gateway (client registrations are loaded at boot, no hot-reload).

Errors

Situation Behavior
Missing/malformed/expired token or invalid signature AuthenticationError → Gateway treats it as UNAUTHENTICATED.
Incorrect aud/iss AuthenticationError → UNAUTHENTICATED (README.md §45 Test 8).
Valid token, but client_id without a matching ClientRegistration UnknownClientError → Gateway responds UNKNOWN_CLIENT on tools/call, empty list on tools/list.