Implement SEP-990 Enterprise Managed OAuth#1593
Implement SEP-990 Enterprise Managed OAuth#1593sagar-okta wants to merge 9 commits intomodelcontextprotocol:mainfrom
Conversation
|
@modelcontextprotocol/client
@modelcontextprotocol/server
@modelcontextprotocol/express
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
b3d31ff to
6d61b6f
Compare
On top of #1593. See /tmp/sdk-xaa-fixes-pr.md for full details. - clientSecret optional in requestJwtAuthorizationGrant - exchangeJwtAuthGrant uses applyClientAuthentication dispatcher (client_secret_basic default) - drop case-sensitive token_type !== 'N_A' check - better PRM error message in authExtensions - IdJagTokenExchangeResponseSchema Zod validation - export applyClientAuthentication + applyBasicAuth + applyPostAuth
On top of modelcontextprotocol#1593. See /tmp/sdk-xaa-fixes-pr.md for full details. - clientSecret optional in requestJwtAuthorizationGrant - exchangeJwtAuthGrant uses applyClientAuthentication dispatcher (client_secret_basic default) - drop case-sensitive token_type !== 'N_A' check - better PRM error message in authExtensions - IdJagTokenExchangeResponseSchema Zod validation - export applyClientAuthentication + applyBasicAuth + applyPostAuth
Adds a handler for the auth/cross-app-access-complete-flow extension
scenario. Uses CrossAppAccessProvider with requestJwtAuthorizationGrant
in the assertion callback to perform the full SEP-990 flow:
1. RFC 9728 PRM discovery (provider)
2. RFC 8693 token exchange at IdP: ID token -> ID-JAG (callback)
3. RFC 7523 JWT bearer at AS: ID-JAG -> access token (provider,
client_secret_basic)
Context schema mirrors conformance/src/schemas/context.ts.
Conflicts:
test/conformance/src/everythingClient.ts
- kept both pre-registration (from modelcontextprotocol#1650) and cross-app-access
discriminated union variants
Also: drop unused OAuthClientInformation import from crossAppAccess.ts
(lint failure from ef35f0b).
- Run prettier on crossAppAccess.ts (applyClientAuthentication call from ef35f0b wasn't formatted). - Remove auth/cross-app-access-complete-flow from expected-failures baseline; it passes now (289/289 in CI).
There is no /crossAppAccess subpath export; everything is re-exported from the package root. Collapse into a single import.
The XAA example in docs/client.md was the only inline (un-typechecked) ts block in the file — every other snippet uses the source= pattern. That's why the /crossAppAccess subpath import broke silently. - Add auth_crossAppAccess region to clientGuide.examples.ts, add CrossAppAccessProvider + discoverAndRequestJwtAuthGrant to the shared imports region. - Replace inline markdown with sourced fence, run sync:snippets. - Drop two @linkcode CrossAppAccessProvider refs in crossAppAccess.ts that TypeDoc can't resolve cross-module; use plain backticks.
| */ | ||
| authMethod?: ClientAuthMethod; | ||
| fetchFn?: FetchLike; | ||
| }): Promise<{ access_token: string; token_type: string; expires_in?: number; scope?: string }> { |
There was a problem hiding this comment.
nit: should this be OAuthTokens instead? That has 2 more fields but seems conceptually the same thing
| issued_token_type: z.literal('urn:ietf:params:oauth:token-type:id-jag'), | ||
| access_token: z.string(), | ||
| token_type: z.string().optional(), | ||
| expires_in: z.number().optional(), |
There was a problem hiding this comment.
nit: should this be z.number().coerce().optional() to match
| const parseResult = OAuthErrorResponseSchema.safeParse(errorBody); | ||
| if (parseResult.success) { | ||
| const { error, error_description } = parseResult.data; | ||
| throw new Error(`Token exchange failed: ${error}${error_description ? ` - ${error_description}` : ''}`); |
There was a problem hiding this comment.
nit: should we use this here to make sure it's a OAuthError?
throw await parseErrorResponse(response)
This PR implements SEP-990 Enterprise Managed Authorization using a provider-based approach with RFC 8693 Token Exchange and RFC 7523 JWT Bearer flows. This enables secure cross-app authentication for MCP clients in enterprise environments where users authenticate with an enterprise IdP.
Related: #1090
Motivation and Context
Enterprise environments often require OAuth flows where users authenticate with a centralized identity provider (IdP), and applications need to securely access protected resources on behalf of those users without storing credentials. SEP-990 addresses this by implementing:
This change is needed to support enterprise customers who need to integrate MCP clients into their existing OAuth infrastructure securely, following the same provider pattern as
ClientCredentialsProviderandPrivateKeyJwtProvider.Implementation Approach
Following PR review feedback, this implementation uses a provider-based approach instead of middleware:
Layer 2 - Utility Functions (
crossAppAccess.ts):requestJwtAuthorizationGrant()- RFC 8693 token exchange at IdPdiscoverAndRequestJwtAuthGrant()- Discovery + token exchangeexchangeJwtAuthGrant()- RFC 7523 JWT bearer grant at MCP serverLayer 3 - Provider Class (
authExtensions.ts):CrossAppAccessProvider- ImplementsOAuthClientProviderinterfaceCore Integration (
auth.ts):OAuthClientProviderinterface with optional URL storage methodsauth()to save discovered URLs for providers that need themHow Has This Been Tested?
Added comprehensive test coverage (40 tests total):
crossAppAccess.test.ts(12 tests):authExtensions.test.ts(16 new tests):Updated client.md:
Breaking Changes
No breaking changes - This is an additive feature:
CrossAppAccessProviderclass (does not modify existing providers)OAuthClientProviderinterface (backward compatible)Types of changes
Checklist
Additional Context
Implementation Details:
New Files:
crossAppAccess.ts(230 lines) - Layer 2 utilities for token exchangecrossAppAccess.test.ts(12 test cases)Modified Files:
authExtensions.ts- AddedCrossAppAccessProviderclass (~80 lines)auth.ts- EnhancedOAuthClientProviderinterface with optional URL storage methodsmiddleware.ts- Removed oldwithCrossAppAccessmiddlewareindex.ts- Added export for crossAppAccess moduleauthExtensions.test.ts- Added 16 provider testsKey Features:
ClientCredentialsProvider,PrivateKeyJwtProvider)Dependencies:
qsdependency for proper OAuth parameter encoding