Skip to content
Merged
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
6 changes: 4 additions & 2 deletions src/scenarios/client/auth/dpop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {
CheckStatus,
SpecReference
} from '../../../types';
import { ScenarioUrls, DRAFT_PROTOCOL_VERSION } from '../../../types';
import { ScenarioUrls } from '../../../types';
import {
createAuthServer,
type DpopTokenRequestObservation
Expand Down Expand Up @@ -145,7 +145,9 @@ export function collapseDuplicateChecks(

export class DPoPClientScenario implements Scenario {
readonly name: string;
readonly source = { introducedIn: DRAFT_PROTOCOL_VERSION } as const;
readonly source = {
extensionId: 'io.modelcontextprotocol/auth/dpop'
} as const;
readonly description: string;

private authServer = new ServerLifecycle();
Expand Down
6 changes: 3 additions & 3 deletions src/scenarios/client/auth/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ describe('WIF JWT-bearer negative tests', () => {
});

// DPoP (SEP-1932): the compliant paths for both postures (auth/dpop and
// auth/dpop-nonce) are covered by the Client Draft Scenarios loop above; these
// auth/dpop-nonce) are covered by the Client Extension Scenarios loop above; these
// are the negative cases, via deliberately-broken example clients. The baseline
// checks (scheme, replay, token-request-proof) are nonce-independent, so those
// negatives run against auth/dpop; the two nonce checks only fire when a
Expand Down Expand Up @@ -419,8 +419,8 @@ describe('DPoP client negative tests (SEP-1932)', () => {
// DPoP nonce-less baseline (SEP-1932): a client that implements NO nonce
// handling still completes DPoP successfully when the server does not require a
// nonce (the common case — server nonces are OPTIONAL, RFC 9449 §8/§9). The
// nonce-capable compliant path is covered by the Client Draft Scenarios loop
// above (auth/dpop is in draftScenariosList).
// nonce-capable compliant path is covered by the Client Extension Scenarios
// loop above (auth/dpop is in extensionScenariosList).
describe('DPoP client nonce-less baseline (SEP-1932)', () => {
test('auth/dpop: nonce-incapable client passes the baseline', async () => {
const runner = new InlineClientRunner(dpopNoNonceClient);
Expand Down
10 changes: 5 additions & 5 deletions src/scenarios/client/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ export const backcompatScenariosList: Scenario[] = [
export const extensionScenariosList: Scenario[] = [
new ClientCredentialsJwtScenario(),
new ClientCredentialsBasicScenario(),
new EnterpriseManagedAuthorizationScenario()
new EnterpriseManagedAuthorizationScenario(),
new DPoPClientScenario(false), // auth/dpop — nonce-less baseline (common case)
new DPoPClientScenario(true), // auth/dpop-nonce — server-required nonce (§8/§9)
new WifJwtBearerScenario()
];

// Draft scenarios (informational - not scored for tier assessment)
Expand All @@ -81,8 +84,5 @@ export const draftScenariosList: Scenario[] = [
new IssParameterWrongIssuerScenario(),
new IssParameterUnexpectedScenario(),
new IssParameterNormalizedVariantScenario(),
new MetadataIssuerMismatchScenario(),
new WifJwtBearerScenario(),
new DPoPClientScenario(false), // auth/dpop — nonce-less baseline (common case)
new DPoPClientScenario(true) // auth/dpop-nonce — server-required nonce (§8/§9)
new MetadataIssuerMismatchScenario()
];
13 changes: 4 additions & 9 deletions src/scenarios/client/auth/wif-jwt-bearer.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import * as jose from 'jose';
import type {
Scenario,
ConformanceCheck,
ScenarioUrls,
SpecVersion
} from '../../../types';
import type { Scenario, ConformanceCheck, ScenarioUrls } from '../../../types';
import type { ScenarioContext } from '../../../mock-server';
import { DRAFT_PROTOCOL_VERSION } from '../../../types';
import { createAuthServer } from './helpers/createAuthServer';
import { createServer } from './helpers/createServer';
import { MockTokenVerifier } from './helpers/mockTokenVerifier';
Expand All @@ -26,8 +20,9 @@ const WIF_CLIENT_ID = 'conformance-wif-workload';

export class WifJwtBearerScenario implements Scenario {
name = 'auth/wif-jwt-bearer';
specVersions: SpecVersion[] = [DRAFT_PROTOCOL_VERSION];
readonly source = { introducedIn: DRAFT_PROTOCOL_VERSION } as const;
readonly source = {
extensionId: 'io.modelcontextprotocol/auth/wif'
} as const;
description =
'Tests the RFC 7523 JWT-bearer grant for workload identity federation (SEP-1933). ' +
'The client must: use grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer, ' +
Expand Down
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ export type ScenarioSpecTag = SpecVersion | 'extension';
export const EXTENSION_IDS = [
'io.modelcontextprotocol/oauth-client-credentials',
'io.modelcontextprotocol/enterprise-managed-authorization',
'io.modelcontextprotocol/auth/dpop',
'io.modelcontextprotocol/auth/wif',
'io.modelcontextprotocol/tasks'
] as const;
export type ExtensionId = (typeof EXTENSION_IDS)[number];
Expand Down
Loading