Private Beta — Remote MCP authentication is currently in private beta. Contact us to get access.
This reference describes the ID-JAG authentication flow. For conceptual understanding of why identity delegation matters, see Why Char Exists and Federated Authentication .
Prerequisites: ID-JAG requires one-time IDP setup . Your IDP admin must register Char as an OAuth client and grant token exchange permissions before the flows described here will work.
Standards
ID-JAG (Identity Assertion Authorization Grant) uses four OAuth/OIDC specifications:
Standard Role in ID-JAG RFC 8693 Token Exchange - Hub obtains ID-JAG from IDP RFC 7523 JWT Bearer Grant - Hub exchanges ID-JAG for access token at MCP server RFC 8707 Resource Indicators - scopes tokens to specific MCP servers RFC 9728 Protected Resource Metadata - MCP servers advertise auth requirements
Authentication Flow
Step 1: Token Exchange Request
The Hub requests an ID-JAG from the IDP:
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:
// Header
{
"typ" : "oauth-id-jag+jwt" ,
"alg" : "RS256" ,
"kid" : "idp-signing-key-id"
}
// Payload
{
"iss" : "https://sso.enterprise.com" ,
"sub" : "[email protected] " ,
"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:
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
{
"access_token" : "eyJhbGciOiJSUzI1NiIs..." ,
"token_type" : "Bearer" ,
"expires_in" : 3600
}
IDP Requirements
ID-JAG authentication requires your identity provider to support:
RFC 8693 Token Exchange — The ability to exchange tokens at the /token endpoint
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 Active development PingFederate ✅ ❓ Unconfirmed Full RFC 8693, ID-JAG unverified — PingOne ✅ ❓ Unconfirmed Full RFC 8693, ID-JAG unverified — Auth0 ✅ ⚠️ Beta XAA Resource App Beta Active development Keycloak ✅ (v26.2+) ❌ Not yet Token Exchange GA, ID-JAG pending #43971 Azure AD / Entra ❌ ❌ Uses proprietary On-Behalf-Of — AWS Cognito ❌ ❌ No RFC 8693 support — Google Workspace ⚠️ Limited ❌ Workload Identity Federation only —
Legend: ✅ = Supported, ⚠️ = Partial/Early Access, ❌ = Not supported, ❓ = UnconfirmedThis matrix reflects the state as of January 2026. ID-JAG is an emerging IETF draft specification (draft-ietf-oauth-identity-assertion-authz-grant ). Provider support is evolving rapidly.
MCP Server Requirements
MCP servers must expose /.well-known/oauth-protected-resource:
{
"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:
// 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 headerMust be oauth-id-jag+jwt issMust match trusted IDP issuer audMust match MCP server’s resource ID client_idMust match expected Tool Hub client expMust be in future (60s clock skew tolerance)
Access Token Validation
MCP servers validate incoming access tokens on protected endpoints:
Claim Validation issMust match MCP server’s token endpoint audMust match MCP server’s resource ID subUser identifier for authorization expMust be in future (60s clock skew)
Optional Claims
Claim Purpose emailUser email address groupsGroup memberships rolesRole assignments scopeGranted 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}
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.
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.
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:
MCP:
See Also
Why Char Exists Architecture and design rationale
Federated Authentication Identity delegation concepts
Trust Boundaries Internal vs external MCP classification
Security Reference Token validation specifications