From bf9039eaa73fbc553b3b2167b43a5ae129ea0a2a Mon Sep 17 00:00:00 2001 From: Christopher Hertel Date: Tue, 18 Aug 2026 02:49:28 +0200 Subject: [PATCH] [Tests] Pin that protected resource metadata advertises no scope of its own SEP-2207 says a resource server must not advertise `offline_access` as a required scope. Nothing in src/ writes it - `scopesSupported` is entirely operator-supplied - so there was nothing to fix, but also nothing stopping a future default from quietly starting to advertise one. --- .../OAuth/ProtectedResourceMetadataTest.php | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/Unit/Server/Transport/Http/OAuth/ProtectedResourceMetadataTest.php b/tests/Unit/Server/Transport/Http/OAuth/ProtectedResourceMetadataTest.php index fbb51a91..5016c73a 100644 --- a/tests/Unit/Server/Transport/Http/OAuth/ProtectedResourceMetadataTest.php +++ b/tests/Unit/Server/Transport/Http/OAuth/ProtectedResourceMetadataTest.php @@ -83,4 +83,33 @@ public function testEmptyAuthorizationServersThrows(): void new ProtectedResourceMetadata([]); } + + #[TestDox('the SDK advertises exactly the scopes it was given, and never adds offline_access')] + public function testScopesAreOperatorSuppliedOnly(): void + { + // SEP-2207: `offline_access` is a refresh-token scope, not something a + // resource requires. Nothing in the SDK injects it — this pins that, so + // a future default cannot quietly start advertising one. + $metadata = new ProtectedResourceMetadata( + resource: 'https://api.example.com/mcp', + authorizationServers: ['https://auth.example.com'], + scopesSupported: ['mcp:read', 'mcp:write'], + ); + + $data = $metadata->jsonSerialize(); + + $this->assertSame(['mcp:read', 'mcp:write'], $data['scopes_supported']); + $this->assertNotContains('offline_access', $data['scopes_supported']); + } + + #[TestDox('no scopes given means no scopes_supported member at all')] + public function testNoScopesMeansNoMember(): void + { + $metadata = new ProtectedResourceMetadata( + resource: 'https://api.example.com/mcp', + authorizationServers: ['https://auth.example.com'], + ); + + $this->assertArrayNotHasKey('scopes_supported', $metadata->jsonSerialize()); + } }