client/auth: register dpop and wif scenarios as extensions (#401)
Move the DPoP scenarios (auth/dpop, auth/dpop-nonce) and the WIF JWT-bearer scenario (auth/wif-jwt-bearer) off the draft spec timeline and register them as the io.modelcontextprotocol/auth/dpop and io.modelcontextprotocol/auth/wif protocol extensions (SEP-2133 reverse-DNS ids), so they run under --suite extensions rather than at --spec-version draft. Claude-Session: https://claude.ai/code/session_012E7BriGaLimJxQr7B7ChiM Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
6e726734f5
commit
ce25103b1b
5 changed files with 18 additions and 19 deletions
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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()
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
@ -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, ' +
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
Loading…
Reference in a new issue