|
| 1 | +package io.micronaut.security.token.jwt.signature.jwks; |
| 2 | + |
| 3 | +import io.micronaut.context.ApplicationContext; |
| 4 | +import io.micronaut.context.annotation.Requires; |
| 5 | +import io.micronaut.http.HttpRequest; |
| 6 | +import io.micronaut.http.HttpResponse; |
| 7 | +import io.micronaut.http.MutableHttpRequest; |
| 8 | +import io.micronaut.http.annotation.Controller; |
| 9 | +import io.micronaut.http.annotation.Filter; |
| 10 | +import io.micronaut.http.annotation.Get; |
| 11 | +import io.micronaut.http.client.BlockingHttpClient; |
| 12 | +import io.micronaut.http.client.HttpClient; |
| 13 | +import io.micronaut.http.filter.ClientFilterChain; |
| 14 | +import io.micronaut.http.filter.HttpClientFilter; |
| 15 | +import io.micronaut.runtime.server.EmbeddedServer; |
| 16 | +import io.micronaut.security.annotation.Secured; |
| 17 | +import io.micronaut.security.rules.SecurityRule; |
| 18 | +import org.junit.jupiter.params.ParameterizedTest; |
| 19 | +import org.junit.jupiter.params.provider.Arguments; |
| 20 | +import org.junit.jupiter.params.provider.MethodSource; |
| 21 | +import org.reactivestreams.Publisher; |
| 22 | +import java.util.ArrayList; |
| 23 | +import java.util.Collections; |
| 24 | +import java.util.List; |
| 25 | +import java.util.Map; |
| 26 | +import java.util.stream.Stream; |
| 27 | + |
| 28 | +import static org.junit.jupiter.api.Assertions.*; |
| 29 | +import static org.junit.jupiter.params.provider.Arguments.arguments; |
| 30 | + |
| 31 | +class JwksRequestDoesNotDuplicateHeadersTest { |
| 32 | + static Stream<Arguments> paramsProvider() { |
| 33 | + return Stream.of( |
| 34 | + arguments(CacheableJwkSetFetcher.class, "micronaut.caches.jwks.expire-after-write", "0s"), |
| 35 | + arguments(ReactorCacheJwkSetFetcher.class, "micronaut.security.token.jwt.signatures.jwks.google.cache-expiration", "0")); |
| 36 | + } |
| 37 | + |
| 38 | + @ParameterizedTest |
| 39 | + @MethodSource("paramsProvider") |
| 40 | + void jwksRequestDoesNotDuplicateHeadersTest(Class fetcherClass, String configName, String configValue) { |
| 41 | + Map<String, Object> authServerConfig = Map.of("spec.name", "JwksRequestDoesNotDuplicateHeadersTestGoogle"); |
| 42 | + try (EmbeddedServer authServer = ApplicationContext.run(EmbeddedServer.class, authServerConfig)) { |
| 43 | + Map<String, Object> serverConfig = Map.of("spec.name", "JwksRequestDoesNotDuplicateHeadersTest", |
| 44 | + "micronaut.http.client.read-timeout", "5m", |
| 45 | + "micronaut.security.token.jwt.signatures.jwks.google.url", |
| 46 | + "http://localhost:" + authServer.getPort() + "/oauth2/v3/certs", |
| 47 | + configName, configValue); |
| 48 | + try (EmbeddedServer server = ApplicationContext.run(EmbeddedServer.class, serverConfig)) { |
| 49 | + assertInstanceOf(fetcherClass, server.getApplicationContext().getBean(JwkSetFetcher.class)); |
| 50 | + HttpClient httpClient = server.getApplicationContext().createBean(HttpClient.class, server.getURL()); |
| 51 | + BlockingHttpClient client = httpClient.toBlocking(); |
| 52 | + assertNotNull(client); |
| 53 | + String jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"; |
| 54 | + int numberOfRequests = 5; |
| 55 | + for (int i = 0; i < numberOfRequests; i++) { |
| 56 | + assertEquals("{\"message\":\"Hello World\"}", client.retrieve(HttpRequest.GET("/").bearerAuth(jwt))); |
| 57 | + } |
| 58 | + assertEquals(5, authServer.getApplicationContext().getBean(GoogleAuthController.class).headers.size()); |
| 59 | + } |
| 60 | + } |
| 61 | + } |
| 62 | + |
| 63 | + @Requires(property = "spec.name", value = "JwksRequestDoesNotDuplicateHeadersTest") |
| 64 | + @Controller |
| 65 | + static class HomeController { |
| 66 | + |
| 67 | + @Secured(SecurityRule.IS_ANONYMOUS) |
| 68 | + @Get |
| 69 | + Map<String, String> index() { |
| 70 | + return Collections.singletonMap("message", "Hello World"); |
| 71 | + } |
| 72 | + } |
| 73 | + |
| 74 | + @Requires(property = "spec.name", value = "JwksRequestDoesNotDuplicateHeadersTest") |
| 75 | + @Filter(patterns = "/oauth2/v3/certs") |
| 76 | + static class AddHeaderFilter implements HttpClientFilter { |
| 77 | + public Publisher<? extends HttpResponse<?>> doFilter(MutableHttpRequest<?> request, ClientFilterChain chain) { |
| 78 | + return chain.proceed(request.header("hdr", "a really long string ...")); |
| 79 | + } |
| 80 | + } |
| 81 | + |
| 82 | + @Requires(property = "spec.name", value = "JwksRequestDoesNotDuplicateHeadersTestGoogle") |
| 83 | + @Controller |
| 84 | + static class GoogleAuthController { |
| 85 | + List<String> headers = new ArrayList<>(); |
| 86 | + @Secured(SecurityRule.IS_ANONYMOUS) |
| 87 | + @Get("/oauth2/v3/certs") |
| 88 | + String index(HttpRequest<?> request) { |
| 89 | + headers.addAll(request.getHeaders().getAll("hdr")); |
| 90 | + return """ |
| 91 | + { |
| 92 | + "keys": [ |
| 93 | + { |
| 94 | + "kty": "RSA", |
| 95 | + "e": "AQAB", |
| 96 | + "use": "sig", |
| 97 | + "kid": "fa072f75784642615087c7182c101341e18f7a3a", |
| 98 | + "n": "pleuF0RyDsETygZn89RpGVFNMxG_hdYVnvbHadvM1tYxs9ghDq93NFxejt--1QlwpLQ3yuVY_CKldkAWgzPVl8-oUBe5xh9jzpLUTqcyrS1aFLuzAe13-OTadUE18wvhz9goQf80rg5IztD_gBePOOBE7eWHGqWLghuMb7cIYjgFxqNFyPn8bF_7k8pQAeHIPua_6_GHhw3ML4msp-aU7O1io3Z4P_Bir_6_C5J9UtWAcJ0Ez0YC5FxOMkh27joO5mUas8krGnFqIJTOgDYXQC1QTu-HOCRNvi6gFMqEkDTP5oBK2cDPDq5L0T8Q0UanSPR0BuOTHesCXnDAdxdyXw", |
| 99 | + "alg": "RS256" |
| 100 | + }, |
| 101 | + { |
| 102 | + "n": "5D9Xb4z8eFr-3Zh3m5GnM_KVqc6rskPL7EMa6lSxNiMJ-PhXGORU-S-QgLmMvHu3vAMfvxz6ph3JZDpdGT68wj-vWqqBudaDYCbnbkjXm6UpcrFMpGAiOS6gACNxpz80JXaO2DPtl9jTN6WyJY9tLHdqRfesfOlwzB0lmVZ8shSDh8usN3vB1KfYuR6Vytly1phaWJr92yMICKUjtXT-0SlrtqDgX_U2Swl4QyZN6rrfuG3F6Fmw-m12Ve_kyoPUb02bbJCSFDnIZsMvRlSZem5nUrs86zDPTWfNcB0LUYG8OgMzOev7r04h_RY2F6K7c8nE2EobYTrH0kw2QIf8vQ", |
| 103 | + "alg": "RS256", |
| 104 | + "kid": "eec534fa5b8caca201ca8d0ff96b54c562210d1e", |
| 105 | + "e": "AQAB", |
| 106 | + "kty": "RSA", |
| 107 | + "use": "sig" |
| 108 | + } |
| 109 | + ] |
| 110 | + } |
| 111 | + """; |
| 112 | + } |
| 113 | + } |
| 114 | +} |
0 commit comments