Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions src/scenarios/client/auth/enterprise-managed-authorization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand All @@ -42,7 +42,7 @@ async function createIdpIdToken(
email: userId,
aud: audience
})
.setProtectedHeader({ alg: 'ES256' })
.setProtectedHeader({ alg: 'RS256' })
.setIssuer(idpIssuer)
.setIssuedAt()
.setExpirationTime('1h')
Expand Down Expand Up @@ -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'
]
Expand Down
33 changes: 33 additions & 0 deletions src/scenarios/client/auth/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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);
Expand Down