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
21 changes: 21 additions & 0 deletions examples/clients/typescript/auth-test-dpop-bearer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env node

import { runDpopClient } from './helpers/dpopClientFlow';
import { runAsCli } from './helpers/cliRunner';

/**
* Broken DPoP client: builds correct per-request proofs but presents the
* DPoP-bound token with the `Bearer` scheme instead of `DPoP`. Isolates a
* failure of sep-1932-client-dpop-auth-scheme (RFC 9449 §7.1).
*/
export async function runClient(serverUrl: string): Promise<void> {
await runDpopClient(serverUrl, {
scheme: 'Bearer',
freshProofPerRequest: true,
sendTokenRequestProof: true,
handleAsNonce: true,
handleRsNonce: true
});
}

runAsCli(runClient, import.meta.url, 'auth-test-dpop-bearer <server-url>');
22 changes: 22 additions & 0 deletions examples/clients/typescript/auth-test-dpop-no-as-nonce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env node

import { runDpopClient } from './helpers/dpopClientFlow';
import { runAsCli } from './helpers/cliRunner';

/**
* Broken DPoP client: ignores the authorization server's `use_dpop_nonce`
* challenge at the token endpoint (RFC 9449 §8) — it does not retry the token
* request with the supplied nonce. Isolates a failure of
* sep-1932-client-as-nonce.
*/
export async function runClient(serverUrl: string): Promise<void> {
await runDpopClient(serverUrl, {
scheme: 'DPoP',
freshProofPerRequest: true,
sendTokenRequestProof: true,
handleAsNonce: false,
handleRsNonce: true
});
}

runAsCli(runClient, import.meta.url, 'auth-test-dpop-no-as-nonce <server-url>');
25 changes: 25 additions & 0 deletions examples/clients/typescript/auth-test-dpop-no-nonce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env node

import { runDpopClient } from './helpers/dpopClientFlow';
import { runAsCli } from './helpers/cliRunner';

/**
* Nonce-incapable but otherwise compliant DPoP client (SEP-1932 / RFC 9449):
* presents the DPoP-bound token with the `DPoP` Authorization scheme and a fresh
* proof on every request, but implements NO `use_dpop_nonce` handling. Against
* the nonce-less `auth/dpop` scenario (where neither server issues a challenge)
* it completes the flow successfully — proving that a client which does not
* support nonces still passes when the server does not require one (the common
* case, since server nonces are OPTIONAL in RFC 9449 §8/§9).
*/
export async function runClient(serverUrl: string): Promise<void> {
await runDpopClient(serverUrl, {
scheme: 'DPoP',
freshProofPerRequest: true,
sendTokenRequestProof: true,
handleAsNonce: false,
handleRsNonce: false
});
}

runAsCli(runClient, import.meta.url, 'auth-test-dpop-no-nonce <server-url>');
22 changes: 22 additions & 0 deletions examples/clients/typescript/auth-test-dpop-no-rs-nonce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env node

import { runDpopClient } from './helpers/dpopClientFlow';
import { runAsCli } from './helpers/cliRunner';

/**
* Broken DPoP client: obtains the token correctly (handles the AS nonce) but
* ignores the MCP server's `use_dpop_nonce` challenge at the resource
* (RFC 9449 §9) — it does not retry the request with the supplied nonce.
* Isolates a failure of sep-1932-client-rs-nonce.
*/
export async function runClient(serverUrl: string): Promise<void> {
await runDpopClient(serverUrl, {
scheme: 'DPoP',
freshProofPerRequest: true,
sendTokenRequestProof: true,
handleAsNonce: true,
handleRsNonce: false
});
}

runAsCli(runClient, import.meta.url, 'auth-test-dpop-no-rs-nonce <server-url>');
27 changes: 27 additions & 0 deletions examples/clients/typescript/auth-test-dpop-no-token-proof.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env node

import { runDpopClient } from './helpers/dpopClientFlow';
import { runAsCli } from './helpers/cliRunner';

/**
* Broken DPoP client: never sends a DPoP proof at the token endpoint, so it
* never asks for a sender-constrained token — the AS falls back to an unbound
* Bearer token. Isolates a failure of sep-1932-client-token-request-proof
* (RFC 9449 §5). Because the resulting token is unbound, the resource-side
* binding check (sep-1932-client-fresh-proof) also fails as a consequence.
*/
export async function runClient(serverUrl: string): Promise<void> {
await runDpopClient(serverUrl, {
scheme: 'DPoP',
freshProofPerRequest: true,
sendTokenRequestProof: false,
handleAsNonce: true,
handleRsNonce: true
});
}

runAsCli(
runClient,
import.meta.url,
'auth-test-dpop-no-token-proof <server-url>'
);
21 changes: 21 additions & 0 deletions examples/clients/typescript/auth-test-dpop-replay.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env node

import { runDpopClient } from './helpers/dpopClientFlow';
import { runAsCli } from './helpers/cliRunner';

/**
* Broken DPoP client: uses the DPoP scheme correctly but reuses a single proof
* (same `jti`) across requests instead of a fresh one each time. Isolates a
* failure of sep-1932-client-fresh-proof.
*/
export async function runClient(serverUrl: string): Promise<void> {
await runDpopClient(serverUrl, {
scheme: 'DPoP',
freshProofPerRequest: false,
sendTokenRequestProof: true,
handleAsNonce: true,
handleRsNonce: true
});
}

runAsCli(runClient, import.meta.url, 'auth-test-dpop-replay <server-url>');
20 changes: 20 additions & 0 deletions examples/clients/typescript/auth-test-dpop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env node

import { runDpopClient } from './helpers/dpopClientFlow';
import { runAsCli } from './helpers/cliRunner';

/**
* Well-behaved DPoP client (SEP-1932 / RFC 9449): presents the DPoP-bound token
* with the `DPoP` Authorization scheme and a fresh proof on every MCP request.
*/
export async function runClient(serverUrl: string): Promise<void> {
await runDpopClient(serverUrl, {
scheme: 'DPoP',
freshProofPerRequest: true,
sendTokenRequestProof: true,
handleAsNonce: true,
handleRsNonce: true
});
}

runAsCli(runClient, import.meta.url, 'auth-test-dpop <server-url>');
8 changes: 8 additions & 0 deletions examples/clients/typescript/everything-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
} from './helpers/withOAuthRetry.js';
import { ConformanceOAuthProvider } from './helpers/ConformanceOAuthProvider.js';
import { runClient as issValidationClient } from './auth-test-iss-validation.js';
import { runClient as dpopClient } from './auth-test-dpop.js';
import { logger } from './helpers/logger.js';

/**
Expand Down Expand Up @@ -854,6 +855,13 @@ registerScenario(
runEnterpriseManagedAuthorization
);

// ============================================================================
// DPoP client conformance (SEP-1932)
// ============================================================================

registerScenario('auth/dpop', dpopClient);
registerScenario('auth/dpop-nonce', dpopClient);

// ============================================================================
// MRTR client conformance (SEP-2322)
// ============================================================================
Expand Down
Loading