Skip to content

Commit 5a93b47

Browse files
committed
test(conformance): wire up cross-app-access-complete-flow scenario
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.
1 parent ef35f0b commit 5a93b47

File tree

1 file changed

+65
-1
lines changed

1 file changed

+65
-1
lines changed

test/conformance/src/everythingClient.ts

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,14 @@
1212
* consolidating all the individual test clients into one.
1313
*/
1414

15-
import { Client, ClientCredentialsProvider, PrivateKeyJwtProvider, StreamableHTTPClientTransport } from '@modelcontextprotocol/client';
15+
import {
16+
Client,
17+
ClientCredentialsProvider,
18+
CrossAppAccessProvider,
19+
PrivateKeyJwtProvider,
20+
requestJwtAuthorizationGrant,
21+
StreamableHTTPClientTransport
22+
} from '@modelcontextprotocol/client';
1623
import * as z from 'zod/v4';
1724

1825
import { logger } from './helpers/logger.js';
@@ -42,6 +49,15 @@ const ClientConformanceContextSchema = z.discriminatedUnion('name', [
4249
name: z.literal('auth/client-credentials-basic'),
4350
client_id: z.string(),
4451
client_secret: z.string()
52+
}),
53+
z.object({
54+
name: z.literal('auth/cross-app-access-complete-flow'),
55+
client_id: z.string(),
56+
client_secret: z.string(),
57+
idp_client_id: z.string(),
58+
idp_id_token: z.string(),
59+
idp_issuer: z.string(),
60+
idp_token_endpoint: z.string()
4561
})
4662
]);
4763

@@ -240,6 +256,54 @@ async function runClientCredentialsBasic(serverUrl: string): Promise<void> {
240256

241257
registerScenario('auth/client-credentials-basic', runClientCredentialsBasic);
242258

259+
/**
260+
* Cross-App Access (SEP-990 Enterprise Managed Authorization).
261+
*
262+
* Exchanges an IdP-issued ID token for an ID-JAG (RFC 8693 token exchange at the IdP),
263+
* then exchanges the ID-JAG for an access token at the AS (RFC 7523 JWT bearer grant
264+
* with client_secret_basic). The provider drives discovery + the JWT bearer step; the
265+
* assertion callback handles the IdP exchange using the context-supplied ID token.
266+
*/
267+
async function runCrossAppAccessCompleteFlow(serverUrl: string): Promise<void> {
268+
const ctx = parseContext();
269+
if (ctx.name !== 'auth/cross-app-access-complete-flow') {
270+
throw new Error(`Expected cross-app-access context, got ${ctx.name}`);
271+
}
272+
273+
const provider = new CrossAppAccessProvider({
274+
clientId: ctx.client_id,
275+
clientSecret: ctx.client_secret,
276+
assertion: async authCtx => {
277+
const result = await requestJwtAuthorizationGrant({
278+
tokenEndpoint: ctx.idp_token_endpoint,
279+
audience: authCtx.authorizationServerUrl,
280+
resource: authCtx.resourceUrl,
281+
idToken: ctx.idp_id_token,
282+
clientId: ctx.idp_client_id,
283+
fetchFn: authCtx.fetchFn
284+
});
285+
return result.jwtAuthGrant;
286+
}
287+
});
288+
289+
const client = new Client({ name: 'conformance-cross-app-access', version: '1.0.0' }, { capabilities: {} });
290+
291+
const transport = new StreamableHTTPClientTransport(new URL(serverUrl), {
292+
authProvider: provider
293+
});
294+
295+
await client.connect(transport);
296+
logger.debug('Successfully connected with cross-app-access auth');
297+
298+
await client.listTools();
299+
logger.debug('Successfully listed tools');
300+
301+
await transport.close();
302+
logger.debug('Connection closed successfully');
303+
}
304+
305+
registerScenario('auth/cross-app-access-complete-flow', runCrossAppAccessCompleteFlow);
306+
243307
// ============================================================================
244308
// Elicitation defaults scenario
245309
// ============================================================================

0 commit comments

Comments
 (0)