> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usechar.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Remote MCP Authentication

> Technical specification for ID-JAG authentication between the Tool Hub and Remote MCP servers

<Note>
  **Private Beta** — Remote MCP authentication is currently in private beta. [Contact us](mailto:alex@mcp-b.ai?subject=Remote%20MCP%20Access) to get access.
</Note>

This reference describes the ID-JAG authentication flow. For conceptual understanding of why identity delegation matters, see [Why Char Exists](/explanation/how-char-works) and [Federated Authentication](/explanation/federated-auth).

<Warning>
  **Prerequisites:** ID-JAG requires [one-time IDP setup](/explanation/enterprise-sso-mcp#setup-requirements). Your IDP admin must register Char as an OAuth client and grant token exchange permissions before the flows described here will work.
</Warning>

## Standards

ID-JAG (Identity Assertion Authorization Grant) uses four OAuth/OIDC specifications:

| Standard                                                  | Role in ID-JAG                                                         |
| --------------------------------------------------------- | ---------------------------------------------------------------------- |
| [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) | Token Exchange - Hub obtains ID-JAG from IDP                           |
| [RFC 7523](https://datatracker.ietf.org/doc/html/rfc7523) | JWT Bearer Grant - Hub exchanges ID-JAG for access token at MCP server |
| [RFC 8707](https://datatracker.ietf.org/doc/html/rfc8707) | Resource Indicators - scopes tokens to specific MCP servers            |
| [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728) | Protected Resource Metadata - MCP servers advertise auth requirements  |

## Authentication Flow

```mermaid theme={null}
sequenceDiagram
    participant User as User Browser
    participant IDP as Identity Provider
    participant Hub as Tool Hub
    participant MCP as Remote MCP Server

    User->>IDP: Authenticate
    IDP-->>User: ID Token (aud: frontend)
    User->>Hub: Connect with ID Token
    Hub->>Hub: Validate token against IDP JWKS

    Note over Hub,MCP: Tool invocation

    Hub->>IDP: Token Exchange (RFC 8693)
    IDP-->>Hub: ID-JAG (aud: mcp-server)

    Hub->>MCP: JWT Bearer Grant (RFC 7523)
    MCP-->>Hub: Access Token

    Hub->>MCP: tools/call + Bearer Token
    MCP-->>Hub: Tool result
```

### Step 1: Token Exchange Request

The Hub requests an ID-JAG from the IDP:

```http theme={null}
POST /oauth/token HTTP/1.1
Host: your-idp.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token={user_id_token}
&subject_token_type=urn:ietf:params:oauth:token-type:id_token
&requested_token_type=urn:ietf:params:oauth:token-type:id-jag
&audience=https://mcp-server.internal.com
&resource=https://mcp-server.internal.com
&scope=mcp:tools:read mcp:tools:call
```

### Step 2: ID-JAG Structure

The IDP returns a JWT with type `oauth-id-jag+jwt`:

```json theme={null}
// Header
{
  "typ": "oauth-id-jag+jwt",
  "alg": "RS256",
  "kid": "idp-signing-key-id"
}
// Payload
{
  "iss": "https://sso.enterprise.com",
  "sub": "user@enterprise.com",
  "aud": "https://mcp-server.internal.com",
  "client_id": "char-tool-hub",
  "resource": "https://mcp-server.internal.com",
  "scope": "mcp:tools:read mcp:tools:call",
  "exp": 1737100000,
  "iat": 1737099700,
  "jti": "unique-assertion-id"
}
```

### Step 3: JWT Bearer Grant

The Hub exchanges the ID-JAG for an access token at the MCP server:

```http theme={null}
POST /oauth/token HTTP/1.1
Host: mcp-server.internal.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&assertion={id_jag_jwt}
&client_id=char-tool-hub
&client_secret={tool_hub_secret}
```

### Step 4: Access Token Response

```json theme={null}
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600
}
```

## IDP Requirements

ID-JAG authentication requires your identity provider to support:

1. **RFC 8693 Token Exchange** — The ability to exchange tokens at the `/token` endpoint
2. **ID-JAG Token Type** — Issuing JWTs with `typ: "oauth-id-jag+jwt"` and accepting `requested_token_type=urn:ietf:params:oauth:token-type:id-jag`

Many providers support the underlying Token Exchange primitive but not the specific ID-JAG profile yet.

| Requirement                   | Notes                           |
| ----------------------------- | ------------------------------- |
| Token Exchange (RFC 8693)     | Required                        |
| ID-JAG Token Type             | Required for full compatibility |
| Configurable resource servers | For audience scoping            |
| JWKS endpoint                 | For signature validation        |

### IDP Support Matrix

ID-JAG requires both RFC 8693 (Token Exchange) and support for the `oauth-id-jag+jwt` token type. Support varies significantly across providers.

| Provider         | RFC 8693   | ID-JAG Token Type | Status                                                           | Tracking                                                    |
| ---------------- | ---------- | ----------------- | ---------------------------------------------------------------- | ----------------------------------------------------------- |
| Okta             | ✅          | ⚠️ Early Access   | [XAA Early Access](https://www.okta.com/saas-security/sign-up/)  | Active development                                          |
| PingFederate     | ✅          | ❓ Unconfirmed     | Full RFC 8693, ID-JAG unverified                                 | —                                                           |
| PingOne          | ✅          | ❓ Unconfirmed     | Full RFC 8693, ID-JAG unverified                                 | —                                                           |
| Auth0            | ✅          | ⚠️ Beta           | [XAA Resource App Beta](https://auth0.com/docs/xaa-resource-app) | Active development                                          |
| Keycloak         | ✅ (v26.2+) | ❌ Not yet         | Token Exchange GA, ID-JAG pending                                | [#43971](https://github.com/keycloak/keycloak/issues/43971) |
| Azure AD / Entra | ❌          | ❌                 | Uses proprietary On-Behalf-Of                                    | —                                                           |
| AWS Cognito      | ❌          | ❌                 | No RFC 8693 support                                              | —                                                           |
| Google Workspace | ⚠️ Limited | ❌                 | Workload Identity Federation only                                | —                                                           |

<Note>
  **Legend:** ✅ = Supported, ⚠️ = Partial/Early Access, ❌ = Not supported, ❓ = Unconfirmed

  This matrix reflects the state as of January 2026. ID-JAG is an emerging IETF draft specification ([draft-ietf-oauth-identity-assertion-authz-grant](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/)). Provider support is evolving rapidly.
</Note>

## MCP Server Requirements

### Protected Resource Metadata (RFC 9728)

MCP servers must expose `/.well-known/oauth-protected-resource`:

```json theme={null}
{
  "resource": "https://mcp-server.internal.com",
  "authorization_servers": ["https://mcp-server.internal.com"],
  "scopes_supported": ["mcp:tools:read", "mcp:tools:call"]
}
```

### Token Endpoint

MCP servers must implement a token endpoint accepting JWT Bearer Grants:

```typescript theme={null}
// Token endpoint implementation
if (url.pathname === '/oauth/token' && request.method === 'POST') {
  const body = await request.formData();
  const grantType = body.get('grant_type');

  // Only accept JWT Bearer Grant
  if (grantType !== 'urn:ietf:params:oauth:grant-type:jwt-bearer') {
    return Response.json({ error: 'unsupported_grant_type' }, { status: 400 });
  }

  const assertion = body.get('assertion') as string;

  // Validate ID-JAG
  const validation = await validateIdJag(assertion, {
    trustedIssuers: [env.IDP_ISSUER],
    expectedAudience: env.MCP_RESOURCE_ID,
    idpJwksUrl: env.IDP_JWKS_URL,
  });

  if (!validation.valid) {
    return Response.json({ error: 'invalid_grant' }, { status: 400 });
  }

  // Issue access token
  const accessToken = await issueAccessToken({
    sub: validation.claims.sub,
    aud: env.MCP_RESOURCE_ID,
    scope: validation.claims.scope,
    exp: Math.floor(Date.now() / 1000) + 3600,
  }, env.SIGNING_KEY);

  return Response.json({
    access_token: accessToken,
    token_type: 'Bearer',
    expires_in: 3600,
  });
}
```

### ID-JAG Validation

| Check        | Requirement                                  |
| ------------ | -------------------------------------------- |
| Signature    | Validate against IDP's JWKS                  |
| `typ` header | Must be `oauth-id-jag+jwt`                   |
| `iss`        | Must match trusted IDP issuer                |
| `aud`        | Must match MCP server's resource ID          |
| `client_id`  | Must match expected Tool Hub client          |
| `exp`        | Must be in future (60s clock skew tolerance) |

### Access Token Validation

MCP servers validate incoming access tokens on protected endpoints:

| Claim | Validation                             |
| ----- | -------------------------------------- |
| `iss` | Must match MCP server's token endpoint |
| `aud` | Must match MCP server's resource ID    |
| `sub` | User identifier for authorization      |
| `exp` | Must be in future (60s clock skew)     |

### Optional Claims

| Claim    | Purpose              |
| -------- | -------------------- |
| `email`  | User email address   |
| `groups` | Group memberships    |
| `roles`  | Role assignments     |
| `scope`  | Granted capabilities |

## Token Properties

| Token Type    | Issuer         | Audience     | Typical Lifetime |
| ------------- | -------------- | ------------ | ---------------- |
| User ID Token | Enterprise IDP | Frontend app | 1 hour           |
| ID-JAG        | Enterprise IDP | MCP server   | 5 minutes        |
| Access Token  | MCP server     | MCP server   | 5-60 minutes     |

## Hub Token Caching

Cache key format: `token:{org_id}:{connector_id}:{user_id}`

```typescript theme={null}
interface CachedToken {
  access_token: string;
  expires_at: number;
  refresh_token?: string;
  scopes: string[];
}
```

Cache TTL: `min(token_expiry - 300, 3600)` seconds.

Tokens are stored in encrypted KV. Never stored in queryable databases.

## Transaction Tokens

For MCP server internal service calls, use downscoped Transaction Tokens:

| Property     | Requirement                        |
| ------------ | ---------------------------------- |
| Audience     | Bound to specific internal service |
| Scope        | Limited to specific operation      |
| Lifetime     | Seconds to minutes                 |
| User context | Embedded in signed claims          |

## External MCP Servers

For MCP servers operated by third parties (not your organization), use **OAuth 2.1 with PKCE**:

* User explicitly consents via OAuth flow
* Requires registration with the external MCP's auth server (not your enterprise IDP)
* User identity is established through the external provider's OAuth, not your SSO

This is the standard OAuth pattern—the user sees a consent screen and authorizes the Tool Hub to act on their behalf with that specific external service.

<Note>
  ID-JAG is for **internal** MCP servers that trust your enterprise IDP. External MCP servers have their own identity systems and require explicit user consent.
</Note>

## Specification Status

| Component                              | Status                          |
| -------------------------------------- | ------------------------------- |
| RFC 8693 (Token Exchange)              | Stable                          |
| RFC 7523 (JWT Bearer Grant)            | Stable                          |
| RFC 8707 (Resource Indicators)         | Stable                          |
| RFC 9728 (Protected Resource Metadata) | Stable                          |
| ID-JAG Draft                           | Draft, implementations emerging |
| MCP Enterprise-Managed Authorization   | Draft                           |

## References

**OAuth/OIDC:**

* [RFC 8693 - Token Exchange](https://datatracker.ietf.org/doc/html/rfc8693)
* [RFC 7523 - JWT Bearer Grant](https://datatracker.ietf.org/doc/html/rfc7523)
* [RFC 8707 - Resource Indicators](https://datatracker.ietf.org/doc/html/rfc8707)
* [RFC 9728 - Protected Resource Metadata](https://datatracker.ietf.org/doc/html/rfc9728)
* [Identity Assertion Authorization Grant (draft)](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/)

**MCP:**

* [MCP Authorization Spec](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization)
* [Enterprise-Managed Authorization Draft](https://github.com/modelcontextprotocol/ext-auth/blob/main/specification/draft/enterprise-managed-authorization.mdx)

## See Also

<CardGroup cols={2}>
  <Card title="Why Char Exists" icon="lightbulb" href="/explanation/how-char-works">
    Architecture and design rationale
  </Card>

  <Card title="Federated Authentication" icon="key" href="/explanation/federated-auth">
    Identity delegation concepts
  </Card>

  <Card title="Trust Boundaries" icon="shield" href="/explanation/trust-boundaries">
    Internal vs external MCP classification
  </Card>

  <Card title="Security Reference" icon="lock" href="/reference/security">
    Token validation specifications
  </Card>
</CardGroup>
