@@ -86,10 +86,15 @@ protected function tearDown(): void {
8686 }
8787
8888 /** Remote discovery response advertising http-sig and $jwksUri. */
89- private function primeDiscovery (string $ jwksUri = self ::JWKS_URI , array $ capabilities = ['http-sig ' ]): void {
89+ private function primeDiscovery (
90+ string $ jwksUri = self ::JWKS_URI ,
91+ array $ capabilities = ['http-sig ' ],
92+ string $ endPoint = 'https://sender.example.org/ocm ' ,
93+ ): void {
9094 $ provider = new OCMProvider ();
9195 $ provider ->setCapabilities ($ capabilities );
9296 $ provider ->setJwksUri ($ jwksUri );
97+ $ provider ->setEndPoint ($ endPoint );
9398 $ this ->discoveryService ->method ('discover ' )->willReturn ($ provider );
9499 }
95100
@@ -166,13 +171,32 @@ public function testGetRemoteKeyRejectsMissingJwksUriWhenHttpSigAdvertised(): vo
166171 $ this ->assertNull ($ this ->signatoryManager ->getRemoteKey ('sender.example.org ' , 'kid ' ));
167172 }
168173
169- public function testGetRemoteKeyRejectsNonHttpsJwksUri (): void {
174+ public function testGetRemoteKeyRejectsHttpJwksUriFromHttpsPeer (): void {
175+ // downgrade guard: http jwksUri from an https peer
170176 $ this ->primeDiscovery (jwksUri: 'http://sender.example.org/ocm/jwks ' );
171177 $ this ->client ->expects ($ this ->never ())->method ('get ' );
172178 $ this ->logger ->expects ($ this ->once ())->method ('warning ' );
173179 $ this ->assertNull ($ this ->signatoryManager ->getRemoteKey ('sender.example.org ' , 'kid ' ));
174180 }
175181
182+ public function testGetRemoteKeyAcceptsHttpJwksUriFromHttpPeer (): void {
183+ // the spec's http fallback for testing setups
184+ $ this ->primeDiscovery (
185+ jwksUri: 'http://sender.example.org/ocm/jwks ' ,
186+ endPoint: 'http://sender.example.org/ocm ' ,
187+ );
188+ $ kid = 'sender.example.org#key1 ' ;
189+ $ this ->client ->expects ($ this ->once ())
190+ ->method ('get ' )
191+ ->with (
192+ $ this ->equalTo ('http://sender.example.org/ocm/jwks ' ),
193+ $ this ->isType ('array ' ),
194+ )
195+ ->willReturn ($ this ->jsonResponse (['keys ' => [$ this ->ecJwk ($ kid )]]));
196+
197+ $ this ->assertNotNull ($ this ->signatoryManager ->getRemoteKey ('sender.example.org ' , $ kid ));
198+ }
199+
176200 public function testGetRemoteKeyReturnsNullWhenDiscoveryFails (): void {
177201 $ this ->discoveryService ->method ('discover ' )
178202 ->willThrowException (new OCMProviderException ('no discovery ' ));
@@ -269,6 +293,62 @@ public function testCacheMissOnNewKidTriggersRefetchOnce(): void {
269293 $ this ->assertNotNull ($ this ->signatoryManager ->getRemoteKey ('sender.example.org ' , 'new ' ));
270294 }
271295
296+ public function testGetRemoteKeyAcceptsHttpsJwksUriFromHttpPeer (): void {
297+ // upgrade from an http-only peer is fine
298+ $ this ->primeDiscovery (
299+ endPoint: 'http://sender.example.org/ocm ' ,
300+ );
301+ $ kid = 'sender.example.org#key1 ' ;
302+ $ this ->client ->expects ($ this ->once ())
303+ ->method ('get ' )
304+ ->with (
305+ $ this ->equalTo (self ::JWKS_URI ),
306+ $ this ->isType ('array ' ),
307+ )
308+ ->willReturn ($ this ->jsonResponse (['keys ' => [$ this ->ecJwk ($ kid )]]));
309+
310+ $ this ->assertNotNull ($ this ->signatoryManager ->getRemoteKey ('sender.example.org ' , $ kid ));
311+ }
312+
313+ public function testGetRemoteKeyAcceptsJwksUriOnDifferentHost (): void {
314+ // the JWK Set may live on a different host than the peer
315+ $ this ->primeDiscovery (
316+ jwksUri: 'https://keys.example.net/ocm/jwks ' ,
317+ endPoint: 'http://sender.example.org/ocm ' ,
318+ );
319+ $ kid = 'sender.example.org#key1 ' ;
320+ $ this ->client ->expects ($ this ->once ())
321+ ->method ('get ' )
322+ ->with (
323+ $ this ->equalTo ('https://keys.example.net/ocm/jwks ' ),
324+ $ this ->isType ('array ' ),
325+ )
326+ ->willReturn ($ this ->jsonResponse (['keys ' => [$ this ->ecJwk ($ kid )]]));
327+
328+ $ this ->assertNotNull ($ this ->signatoryManager ->getRemoteKey ('sender.example.org ' , $ kid ));
329+ }
330+
331+ public function testGetLocalJwksUriUsesHttpsByDefault (): void {
332+ $ this ->urlGenerator ->method ('getAbsoluteURL ' )
333+ ->willReturnCallback (fn (string $ path ) => 'https://sender.example.org ' . $ path );
334+
335+ $ this ->assertSame (
336+ 'https://sender.example.org/.well-known/jwks.json ' ,
337+ $ this ->signatoryManager ->getLocalJwksUri (),
338+ );
339+ }
340+
341+ public function testGetLocalJwksUriFollowsHttpInstanceScheme (): void {
342+ // http-only deployments must advertise a fetchable jwksUri
343+ $ this ->urlGenerator ->method ('getAbsoluteURL ' )
344+ ->willReturnCallback (fn (string $ path ) => 'http://localhost:8180 ' . $ path );
345+
346+ $ this ->assertSame (
347+ 'http://localhost:8180/.well-known/jwks.json ' ,
348+ $ this ->signatoryManager ->getLocalJwksUri (),
349+ );
350+ }
351+
272352 private function respondWith (array $ body ): void {
273353 $ this ->client ->method ('get ' )->willReturn ($ this ->jsonResponse ($ body ));
274354 }
0 commit comments