From a18ed07da2de8038fa6df11cf25b7e2cead40657 Mon Sep 17 00:00:00 2001 From: Mitch Fultz <57411549+fitchmultz@users.noreply.github.com> Date: Thu, 5 Nov 2026 12:43:58 -0700 Subject: [PATCH] Fix enterprise IdP metadata for strict OIDC discovery --- .../auth/enterprise-managed-authorization.ts | 9 +++-- src/scenarios/client/auth/index.test.ts | 33 +++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/scenarios/client/auth/enterprise-managed-authorization.ts b/src/scenarios/client/auth/enterprise-managed-authorization.ts index eff5e255..d411b7c4 100644 --- a/src/scenarios/client/auth/enterprise-managed-authorization.ts +++ b/src/scenarios/client/auth/enterprise-managed-authorization.ts @@ -16,13 +16,13 @@ const IDP_CLIENT_ID = 'conformance-test-idp-client'; const DEMO_USER_ID = 'demo-user@example.com'; /** - * Generate an EC P-256 keypair for IDP ID token signing. + * Generate an RSA keypair for the OIDC-required RS256 ID token algorithm. */ async function generateIdpKeypair(): Promise<{ publicKey: CryptoKey; privateKey: CryptoKey; }> { - const { publicKey, privateKey } = await jose.generateKeyPair('ES256', { + const { publicKey, privateKey } = await jose.generateKeyPair('RS256', { extractable: true }); return { publicKey, privateKey }; @@ -42,7 +42,7 @@ async function createIdpIdToken( email: userId, aud: audience }) - .setProtectedHeader({ alg: 'ES256' }) + .setProtectedHeader({ alg: 'RS256' }) .setIssuer(idpIssuer) .setIssuedAt() .setExpirationTime('1h') @@ -165,6 +165,9 @@ export class EnterpriseManagedAuthorizationScenario implements Scenario { authorization_endpoint: `${this.idpServer.getUrl()}/authorize`, token_endpoint: `${this.idpServer.getUrl()}/token`, jwks_uri: `${this.idpServer.getUrl()}/.well-known/jwks.json`, + response_types_supported: ['id_token'], + subject_types_supported: ['public'], + id_token_signing_alg_values_supported: ['RS256'], grant_types_supported: [ 'urn:ietf:params:oauth:grant-type:token-exchange' ] diff --git a/src/scenarios/client/auth/index.test.ts b/src/scenarios/client/auth/index.test.ts index 41c84ae8..59817f0c 100644 --- a/src/scenarios/client/auth/index.test.ts +++ b/src/scenarios/client/auth/index.test.ts @@ -42,6 +42,8 @@ import { setLogLevel } from '../../../../examples/clients/typescript/helpers/log import { DRAFT_PROTOCOL_VERSION } from '../../../types'; import { testScenarioContext } from '../../../mock-server/testing'; import { ClientConformanceContextSchema } from '../../../schemas/context'; +import { discoverAuthorizationServerMetadata } from '@modelcontextprotocol/sdk/client/auth.js'; +import { decodeProtectedHeader } from 'jose'; beforeAll(() => { setLogLevel('error'); @@ -163,6 +165,37 @@ describe('auth/pre-registration context', () => { }); }); +test('enterprise authorization serves discoverable IdP metadata', async () => { + const scenario = extensionScenariosList.find( + (entry) => entry.name === 'auth/enterprise-managed-authorization' + ); + if (!scenario) { + throw new Error('Enterprise authorization scenario not found'); + } + const urls = await scenario.start(testScenarioContext()); + try { + const context = ClientConformanceContextSchema.parse({ + name: scenario.name, + ...urls.context + }); + if (context.name !== 'auth/enterprise-managed-authorization') { + throw new Error(`Unexpected context variant: ${context.name}`); + } + const metadata = await discoverAuthorizationServerMetadata( + context.idp_issuer + ); + expect(metadata).toMatchObject({ + issuer: context.idp_issuer, + token_endpoint: context.idp_token_endpoint, + subject_types_supported: ['public'], + id_token_signing_alg_values_supported: ['RS256'] + }); + expect(decodeProtectedHeader(context.idp_id_token).alg).toBe('RS256'); + } finally { + await scenario.stop(); + } +}); + describe('Negative tests', () => { test('bad client requests root PRM location', async () => { const runner = new InlineClientRunner(badPrmClient);