conformance/src/scenarios/client/auth/index.test.ts
Paul Carleton 5c151133bb
[auth] Scenarios for scope selection (#36)
* wip scope change

* rm useless bool

* fix-me: package changes for linking

* negative test

* negative test for multiple scopes

* fix displays of warnings

* wip step-up

* negative test for step up auth

* move warning into main request flow

* clean up more checks

* cleanup step-up scenario

* cleanup and simplify

* cleanup middleware

* inline negative tests

* inline the other test

* refactor logging and inlining for standalone execution too

* skip pending tests
2025-11-18 17:16:23 +00:00

79 lines
3 KiB
TypeScript

import { authScenariosList } from './index.js';
import {
runClientAgainstScenario,
InlineClientRunner
} from './test_helpers/testClient.js';
import { runClient as goodClient } from '../../../../examples/clients/typescript/auth-test.js';
import { runClient as badPrmClient } from '../../../../examples/clients/typescript/auth-test-bad-prm.js';
import { runClient as ignoreScopeClient } from '../../../../examples/clients/typescript/auth-test-ignore-scope.js';
import { runClient as partialScopesClient } from '../../../../examples/clients/typescript/auth-test-partial-scopes.js';
import { runClient as ignore403Client } from '../../../../examples/clients/typescript/auth-test-ignore-403.js';
import { setLogLevel } from '../../../../examples/clients/typescript/helpers/logger.js';
beforeAll(() => {
setLogLevel('error');
});
const skipScenarios = new Set<string>([
// Waiting on typescript-sdk support in bearerAuth middleware to include
// scope in WWW-Authenticate header
// https://github.com/modelcontextprotocol/typescript-sdk/pull/1133
'auth/scope-from-www-authenticate',
// Waiting on typescript-sdk support for using scopes_supported from PRM
// to request scopes.
// https://github.com/modelcontextprotocol/typescript-sdk/pull/1133
'auth/scope-from-scopes-supported'
]);
describe('Client Auth Scenarios', () => {
// Generate individual test for each auth scenario
for (const scenario of authScenariosList) {
test(`${scenario.name} passes`, async () => {
if (skipScenarios.has(scenario.name)) {
// TODO: skip in a native way?
return;
}
const runner = new InlineClientRunner(goodClient);
await runClientAgainstScenario(runner, scenario.name);
});
}
});
describe('Negative tests', () => {
test('bad client requests root PRM location', async () => {
const runner = new InlineClientRunner(badPrmClient);
await runClientAgainstScenario(runner, 'auth/basic-dcr', [
'prm-priority-order'
]);
});
test('client ignores scope from WWW-Authenticate header', async () => {
const runner = new InlineClientRunner(ignoreScopeClient);
await runClientAgainstScenario(runner, 'auth/scope-from-www-authenticate', [
'scope-from-www-authenticate'
]);
});
test('client only requests subset of scopes_supported', async () => {
const runner = new InlineClientRunner(partialScopesClient);
await runClientAgainstScenario(runner, 'auth/scope-from-scopes-supported', [
'scope-from-scopes-supported'
]);
});
test('client requests scope even if scopes_supported is empty', async () => {
const runner = new InlineClientRunner(partialScopesClient);
await runClientAgainstScenario(
runner,
'auth/scope-omitted-when-undefined',
['scope-omitted-when-undefined']
);
});
test('client only responds to 401, not 403', async () => {
const runner = new InlineClientRunner(ignore403Client);
await runClientAgainstScenario(runner, 'auth/scope-step-up', [
'scope-step-up-escalation'
]);
});
});