Skip to main content
Early access. This feature is in early access, which means it’s undergoing ongoing testing and development while we gather feedback, validate functionality, and improve outputs. Contact the C1 Support team if you’d like to try it out or share feedback.
This page is for a developer making an MCP server accept C1-issued tokens, whether that server is an internal application your organization builds or a SaaS your organization runs. The C1 admin registers your server inside C1; the work described here is the other side, done at your authorization server. Enterprise-managed authorization is built on the open Cross-App Access (XAA) standard, and the token C1 issues is an ID-JAG; both are used directly below because they’re what you implement against.

What your MCP server needs to do

To support enterprise-managed authorization, your server does two things:
  • Accept C1 as a trusted token issuer.
  • Exchange the tokens C1 issues for your own access tokens, using the JWT bearer grant.
C1 issues an ID-JAG, a short-lived JWT whose audience is your authorization server’s issuer. Your authorization server verifies C1’s signature, confirms the token is meant for it, and returns one of your own access tokens that the agent then uses to call your API.

Trust C1 as an issuer

Configure your authorization server to recognize C1 as a token issuer and to advertise the grant C1 relies on.
1
Add C1’s issuer URL and its JWKS URL to your authorization server’s trusted-issuer configuration. The issuer is the tenant URL, and the keys are published at the tenant’s JWKS endpoint:
Issuer:  https://<your-tenant>.conductor.one
JWKS:    https://<your-tenant>.conductor.one/auth/v1/jwks
Your authorization server verifies the ID-JAG signature against the keys at the JWKS URL.
2
Register the agent’s client at your authorization server. The client_id claim in the ID-JAG names the agent’s client; the client ID you assign at your authorization server must match it so the same client is recognized on both sides. The client authenticates to your authorization server with its own credentials on the JWT bearer request; you choose which client authentication method to require.
3
Advertise the JWT bearer grant (urn:ietf:params:oauth:grant-type:jwt-bearer) in your authorization-server metadata, as defined by RFC 8414. C1 discovers this metadata to confirm your authorization server can redeem an ID-JAG.
4
Serve protected-resource metadata from your system, as defined by RFC 9728. This is how C1 discovers which authorization server protects your system.

Verify the token

When your authorization server receives an ID-JAG on a JWT bearer grant, verify each of the following before issuing your own access token:
CheckExpected value
Token type header (typ)oauth-id-jag+jwt
Audience (aud)Exactly your authorization server’s issuer, and only that value
ClientMatches the client authenticating the JWT bearer request
SignatureVerifies against C1’s published keys at the JWKS URL
Scope (scope)Issue an access token carrying only the scopes in the ID-JAG. You may narrow them, but don’t add any.

Requesting scopes (and omitting scope)

The ID-JAG token-exchange scope parameter is optional (RFC 8693 §2.1).
  • scope omitted (or blank): C1 grants the subject’s default full set at the resource server — every enabled scope the subject is actually entitled to (enabled-at-RS ∩ subject-held). This is the path most silent clients use (for example, Claude Code’s silent exchange sends no scope).
  • scope provided: the grant is narrowed to requested ∩ enabled ∩ held. A request can drop scopes but never widen beyond what the subject holds; un-held or not-enabled scopes are silently dropped.
  • If the subject holds no scopes at the resource server, the exchange is denied (invalid_grant) whether or not scope was sent — an omitted scope is never a free pass.
The issued ID-JAG’s scope claim always reflects exactly the granted set.

Identify the user

The ID-JAG identifies the C1 user the agent is acting for. Use these claims to resolve the token to an account in your system.
ClaimWhat it carries
subAn opaque, C1-minted subject pseudonym — a bare random URL-safe identifier with no internal structure, never the user’s raw C1 ID and with no user: prefix. By default it is pairwise: stable for your system, but a different value at every other system, so the same user can’t be correlated across systems. (A C1 admin can opt your system into a global pseudonym instead — the same value for that user everywhere.) For your system it is consistent across token types: the same value appears whether it’s in a C1 ID token issued to your system or in an ID-JAG for your system, so if you also do direct C1 SSO the subjects match. It is not the user’s email address.
emailThe user’s email address. C1 includes it by default so you can link the token to an existing account or provision one just in time.
auth_timeWhen the user last authenticated, when that information is available.
Key your local account on the full opaque sub — at one system, iss + sub is a stable, durable account key. Store it verbatim; never parse it or expect a raw C1 user ID inside it. Because the sub is pairwise by default, it isn’t a cross-system identifier, so use email to link accounts across systems or to provision one just in time when you don’t already have a mapping. C1 does not send a SAML NameID (sub_id) or a resource-server-specific user identifier (aud_sub) in this release. If your system resolves users by one of those, contact the C1 Support team.

Signing algorithms C1 can use

C1 can sign ID-JAGs with ES256 (the default), RS256, or EdDSA. Pick the algorithm your authorization server verifies, and tell your C1 admin which one to use for your system. C1 uses the chosen algorithm with no silent fallback, so the algorithm your admin configures must be one your authorization server accepts.

Example

The following are taken from a verified end-to-end exchange against a live C1 tenant. The protected-resource metadata document shows the fields C1 reads during discovery, and the decoded ID-JAG shows the claims your authorization server verifies.
The resource in your protected-resource metadata must equal the exact URL the agent calls, including any path — not just the origin. If your MCP server is at https://api.example.com/mcp, serve the metadata at https://api.example.com/.well-known/oauth-protected-resource/mcp with "resource": "https://api.example.com/mcp". A bare-origin resource (e.g. https://api.example.com) causes the client’s discovery to fail with a resource mismatch.
{
  "resource": "https://api.example.com/mcp",
  "authorization_servers": [
    "https://api.example.com"
  ],
  "jwks_uri": "https://api.example.com/jwks",
  "scopes_supported": [
    "read:widgets"
  ],
  "bearer_methods_supported": [
    "header"
  ]
}
sub is an opaque pseudonym, not a C1 user ID. The sub above (3FFshhPuwk7lbS1M0nESX4KUIxs) is a bare, C1-minted identifier with no internal structure — by default it’s unique to your system, so don’t parse it and don’t expect to see a raw C1 user ID. Store it verbatim as your account key. (email is included when available; the auth_time claim is present when C1 has that information.)
Advertise an authorization_endpoint in your authorization-server metadata even though the JWT bearer grant does not use it. Some clients validate the RFC 8414 metadata document for a well-formed authorization_endpoint before they will use the JWT bearer grant, and reject the document if it is absent.