Skip to content

Commit 2ec3e36

Browse files
committed
add cimd test and negative test
1 parent 5c15113 commit 2ec3e36

File tree

7 files changed

+48
-10
lines changed

7 files changed

+48
-10
lines changed

examples/clients/typescript/auth-test.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,18 @@
22

33
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
44
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
5-
import { withOAuthRetry } from './helpers/withOAuthRetry.js';
5+
import { withOAuthRetry, handle401 } from './helpers/withOAuthRetry.js';
66
import { runAsCli } from './helpers/cliRunner.js';
77
import { logger } from './helpers/logger.js';
88

9+
/**
10+
* Fixed client metadata URL for CIMD conformance tests.
11+
* When server supports client_id_metadata_document_supported, this URL
12+
* will be used as the client_id instead of doing dynamic registration.
13+
*/
14+
const CIMD_CLIENT_METADATA_URL =
15+
'https://conformance-test.local/client-metadata.json';
16+
917
/**
1018
* Well-behaved auth client that follows all OAuth protocols correctly.
1119
*/
@@ -17,7 +25,9 @@ export async function runClient(serverUrl: string): Promise<void> {
1725

1826
const oauthFetch = withOAuthRetry(
1927
'test-auth-client',
20-
new URL(serverUrl)
28+
new URL(serverUrl),
29+
handle401,
30+
CIMD_CLIENT_METADATA_URL
2131
)(fetch);
2232

2333
const transport = new StreamableHTTPClientTransport(new URL(serverUrl), {

examples/clients/typescript/helpers/ConformanceOAuthProvider.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,11 @@ export class ConformanceOAuthProvider implements OAuthClientProvider {
2727
return this._clientMetadata;
2828
}
2929

30+
get clientMetadataUrl(): string | undefined {
31+
return this._clientMetadataUrl?.toString();
32+
}
33+
3034
clientInformation(): OAuthClientInformation | undefined {
31-
if (this._clientMetadataUrl) {
32-
console.log('Using client ID metadata URL');
33-
return {
34-
client_id: this._clientMetadataUrl.toString()
35-
};
36-
}
3735
return this._clientInformation;
3836
}
3937

examples/clients/typescript/helpers/withOAuthRetry.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,16 @@ export const handle401 = async (
6060
export const withOAuthRetry = (
6161
clientName: string,
6262
baseUrl?: string | URL,
63-
handle401Fn: typeof handle401 = handle401
63+
handle401Fn: typeof handle401 = handle401,
64+
clientMetadataUrl?: string
6465
): Middleware => {
6566
const provider = new ConformanceOAuthProvider(
6667
'http://localhost:3000/callback',
6768
{
6869
client_name: clientName,
6970
redirect_uris: ['http://localhost:3000/callback']
70-
}
71+
},
72+
clientMetadataUrl
7173
);
7274
return (next: FetchLike) => {
7375
return async (

src/scenarios/client/auth/helpers/createAuthServer.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ export interface AuthServerOptions {
1010
loggingEnabled?: boolean;
1111
routePrefix?: string;
1212
scopesSupported?: string[];
13+
clientIdMetadataDocumentSupported?: boolean;
1314
tokenVerifier?: MockTokenVerifier;
1415
onTokenRequest?: (requestData: {
1516
scope?: string;
1617
grantType: string;
1718
timestamp: string;
1819
}) => { token: string; scopes: string[] };
1920
onAuthorizationRequest?: (requestData: {
21+
clientId?: string;
2022
scope?: string;
2123
timestamp: string;
2224
}) => void;
@@ -33,6 +35,7 @@ export function createAuthServer(
3335
loggingEnabled = true,
3436
routePrefix = '',
3537
scopesSupported,
38+
clientIdMetadataDocumentSupported,
3639
tokenVerifier,
3740
onTokenRequest,
3841
onAuthorizationRequest
@@ -93,6 +96,12 @@ export function createAuthServer(
9396
metadata.scopes_supported = scopesSupported;
9497
}
9598

99+
// Add client_id_metadata_document_supported if provided
100+
if (clientIdMetadataDocumentSupported !== undefined) {
101+
metadata.client_id_metadata_document_supported =
102+
clientIdMetadataDocumentSupported;
103+
}
104+
96105
// Add OpenID Configuration specific fields
97106
if (isOpenIdConfiguration) {
98107
metadata.jwks_uri = `${getAuthBaseUrl()}/.well-known/jwks.json`;
@@ -123,6 +132,7 @@ export function createAuthServer(
123132

124133
if (onAuthorizationRequest) {
125134
onAuthorizationRequest({
135+
clientId: req.query.client_id as string | undefined,
126136
scope: scopeParam,
127137
timestamp
128138
});

src/scenarios/client/auth/index.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
} from './test_helpers/testClient.js';
66
import { runClient as goodClient } from '../../../../examples/clients/typescript/auth-test.js';
77
import { runClient as badPrmClient } from '../../../../examples/clients/typescript/auth-test-bad-prm.js';
8+
import { runClient as noCimdClient } from '../../../../examples/clients/typescript/auth-test-no-cimd.js';
89
import { runClient as ignoreScopeClient } from '../../../../examples/clients/typescript/auth-test-ignore-scope.js';
910
import { runClient as partialScopesClient } from '../../../../examples/clients/typescript/auth-test-partial-scopes.js';
1011
import { runClient as ignore403Client } from '../../../../examples/clients/typescript/auth-test-ignore-403.js';
@@ -76,4 +77,11 @@ describe('Negative tests', () => {
7677
'scope-step-up-escalation'
7778
]);
7879
});
80+
81+
test('client uses DCR instead of CIMD when server supports it', async () => {
82+
const runner = new InlineClientRunner(noCimdClient);
83+
await runClientAgainstScenario(runner, 'auth/basic-cimd', [
84+
'cimd-client-id-used'
85+
]);
86+
});
7987
});

src/scenarios/client/auth/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Scenario } from '../../../types';
22
import { AuthBasicDCRScenario } from './basic-dcr.js';
3+
import { AuthBasicCIMDScenario } from './basic-cimd.js';
34
import {
45
AuthBasicMetadataVar1Scenario,
56
AuthBasicMetadataVar2Scenario,
@@ -18,6 +19,7 @@ import {
1819

1920
export const authScenariosList: Scenario[] = [
2021
new AuthBasicDCRScenario(),
22+
new AuthBasicCIMDScenario(),
2123
new AuthBasicMetadataVar1Scenario(),
2224
new AuthBasicMetadataVar2Scenario(),
2325
new AuthBasicMetadataVar3Scenario(),

src/scenarios/client/auth/spec-references.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,13 @@ export const SpecReferences: { [key: string]: SpecReference } = {
5252
MCP_AUTH_ERROR_HANDLING: {
5353
id: 'MCP-Auth-error-handling',
5454
url: 'https://modelcontextprotocol.io/specification/draft/basic/authorization#error-handling'
55+
},
56+
MCP_CLIENT_ID_METADATA_DOCUMENTS: {
57+
id: 'MCP-Client-ID-Metadata-Documents',
58+
url: 'https://modelcontextprotocol.io/specification/draft/basic/authorization#client-id-metadata-documents'
59+
},
60+
IETF_CIMD: {
61+
id: 'IETF-OAuth-Client-ID-Metadata-Document',
62+
url: 'https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00'
5563
}
5664
};

0 commit comments

Comments
 (0)