From 5bfe8527b92373ab6f98c8b900b95fabaaf356ec Mon Sep 17 00:00:00 2001 From: Nicklas Wallgren Date: Sun, 10 Aug 2025 17:45:16 +0200 Subject: [PATCH 1/2] fix: handle content types other than application json and update test.p12 for test environment. --- .../BankIdApiUnexpectedResponseException.java | 26 ++- .../bankid/internal/http/JsonBodyHandler.java | 22 ++- src/main/resources/ca.prod.crt | 33 ++++ src/main/resources/test.p12 | Bin 2829 -> 2979 bytes .../http/JsonBodyHandlerUnitTest.java | 172 ++++++++++++++++++ 5 files changed, 249 insertions(+), 4 deletions(-) create mode 100644 src/main/resources/ca.prod.crt create mode 100644 src/test/java/dev/eidentification/bankid/internal/http/JsonBodyHandlerUnitTest.java diff --git a/src/main/java/dev/eidentification/bankid/exceptions/BankIdApiUnexpectedResponseException.java b/src/main/java/dev/eidentification/bankid/exceptions/BankIdApiUnexpectedResponseException.java index cadb4b0..c3d7af0 100644 --- a/src/main/java/dev/eidentification/bankid/exceptions/BankIdApiUnexpectedResponseException.java +++ b/src/main/java/dev/eidentification/bankid/exceptions/BankIdApiUnexpectedResponseException.java @@ -1,5 +1,7 @@ package dev.eidentification.bankid.exceptions; +import org.jspecify.annotations.Nullable; + import java.net.http.HttpResponse; /** @@ -18,8 +20,14 @@ public final class BankIdApiUnexpectedResponseException extends BankIdException private final HttpResponse.ResponseInfo responseInfo; private final String responseBody; - private BankIdApiUnexpectedResponseException(final HttpResponse.ResponseInfo responseInfo, final String responseBody, final Throwable cause) { - super(cause); + private BankIdApiUnexpectedResponseException(final HttpResponse.ResponseInfo responseInfo, final String responseBody) { + super("BankId API returned an unexpected response. Body: " + responseBody); + this.responseInfo = responseInfo; + this.responseBody = responseBody; + } + + private BankIdApiUnexpectedResponseException(final HttpResponse.ResponseInfo responseInfo, final String responseBody, @Nullable final Throwable cause) { + super("BankId API returned an unexpected response. Body: " + responseBody, cause); this.responseInfo = responseInfo; this.responseBody = responseBody; } @@ -32,10 +40,22 @@ private BankIdApiUnexpectedResponseException(final HttpResponse.ResponseInfo res * @param cause The cause of the exception. * @return A new instance of BankIdApiUnexpectedResponseException. */ - public static BankIdApiUnexpectedResponseException of(final HttpResponse.ResponseInfo responseInfo, final String responseBody, final Throwable cause) { + public static BankIdApiUnexpectedResponseException of(final HttpResponse.ResponseInfo responseInfo, final String responseBody, + @Nullable final Throwable cause) { return new BankIdApiUnexpectedResponseException(responseInfo, responseBody, cause); } + /** + * Creates a new instance of BankIdApiUnexpectedResponseException. + * + * @param responseInfo The response information associated with the exception. + * @param responseBody The response body associated with the exception. + * @return A new instance of BankIdApiUnexpectedResponseException. + */ + public static BankIdApiUnexpectedResponseException of(final HttpResponse.ResponseInfo responseInfo, final String responseBody) { + return new BankIdApiUnexpectedResponseException(responseInfo, responseBody); + } + /** * Retrieves the response information associated with an exception thrown during interaction with the BankId system. * diff --git a/src/main/java/dev/eidentification/bankid/internal/http/JsonBodyHandler.java b/src/main/java/dev/eidentification/bankid/internal/http/JsonBodyHandler.java index 0e66c08..03e0fe9 100644 --- a/src/main/java/dev/eidentification/bankid/internal/http/JsonBodyHandler.java +++ b/src/main/java/dev/eidentification/bankid/internal/http/JsonBodyHandler.java @@ -1,15 +1,16 @@ package dev.eidentification.bankid.internal.http; import com.fasterxml.jackson.databind.ObjectMapper; -import dev.eidentification.bankid.internal.annotations.Internal; import dev.eidentification.bankid.client.response.ErrorResponse; import dev.eidentification.bankid.client.response.Response; import dev.eidentification.bankid.exceptions.BankIdApiErrorException; import dev.eidentification.bankid.exceptions.BankIdApiUnexpectedResponseException; +import dev.eidentification.bankid.internal.annotations.Internal; import java.io.InputStream; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; +import java.util.Optional; @Internal public class JsonBodyHandler implements HttpResponse.BodyHandler { @@ -41,6 +42,16 @@ public HttpResponse.BodySubscriber apply(final HttpResponse.ResponseInfo resp return HttpResponse.BodySubscribers.mapping(upstream, inputStream -> inputStreamTo(responseClazz, responseInfo, inputStream)); } + final Optional optionalContentType = responseInfo.headers() + .firstValue("Content-Type") + .map(String::toLowerCase); + + if (optionalContentType.isEmpty() || !optionalContentType.get().contains("application/json")) { + return HttpResponse.BodySubscribers.mapping(upstream, inputStream -> { + throw BankIdApiUnexpectedResponseException.of(responseInfo, inputStreamToString(responseInfo, inputStream)); + }); + } + return HttpResponse.BodySubscribers.mapping(upstream, inputStream -> { throw BankIdApiErrorException.of(inputStreamTo(errorResponseClazz, responseInfo, inputStream)); }); @@ -58,4 +69,13 @@ private T inputStreamTo(final Class targetType, final HttpResponse.Respon } } + private static String inputStreamToString(final HttpResponse.ResponseInfo responseInfo, final InputStream inputStream) { + String body = ""; + + try (final InputStream stream = inputStream) { + return new String(stream.readAllBytes(), StandardCharsets.UTF_8); + } catch (final Exception e) { + throw BankIdApiUnexpectedResponseException.of(responseInfo, body, e); + } + } } diff --git a/src/main/resources/ca.prod.crt b/src/main/resources/ca.prod.crt new file mode 100644 index 0000000..291b5d7 --- /dev/null +++ b/src/main/resources/ca.prod.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- \ No newline at end of file diff --git a/src/main/resources/test.p12 b/src/main/resources/test.p12 index f678de8e6fb70a998f9f2fae4901f412a6a3970e..706092f1babe1777ed81949957892f7a9568f593 100644 GIT binary patch literal 2979 zcmai$XEYlO7sn+{B58O~yQon!icmFLyGD!JTWXKms#a|wHnkh|*gINktG!oIv}T1$ z)h4uRkJoeF_w@Prez@nJ`}?1J?zi8$NHjbL2)Kqs!}TFBM8usoe+&gpwf1 zAkhC_1H!;0NEQgpBH|Ii_8JI)08#GpbC5}1C=Ykc&r5Er~BZ@fHmvI$+YjuAej}>(sfh=a4ev)Z}LL|U69sXgBj~r^ z2eQpOvJZ5Esx^b+%Y;xj;udbL@pPDmafk=~K)&MjD3FJ&kTPZQH&WtrC9|3txV2AA zVxp(neV|^(P7@Tv=(W~s6dn@dk--`qT#*f7yZ0>#h`M@aSu0} z`swvK2r)1=(F7V(&Cc_shEY6`-zdt~QR2n^6?41sD3$d?;K?{cOXL zNO1Jj`t|k#3E`IOUyLya*>F)w8{?LR7e0!dB^=_+Uh+W=0*L#QI`hfatcQg?Q4PUq z`iRw-A8wi|l}aE3X3CZ3sa#SRu+|cEhix)7IE8j-LC78~B@4N4w9yr*4W}Ku8_>0C zR^wxrQt;H_NB^p8B#UNJh>cz_a?+d_S_Zo1(U`4tZ%qPn{5DlK=VH*kZ1| z<2JPw>(-HHETTfe>EaQjg3>|ZzT?@Xg?>xqlVG1Lt9SV&n@N;337hV>uz?e zu~6D`Fe#@!CgcBG556km;~o#|ChfODVa3p$g7En44;@n%7ZaRrOH{Q^_2c9!$9slHA*5)vXIZVEBn+PJ5;USWwPd@2jiy{+6FiJL><_9?3sk zf-7%oz5HIo18$U6eg<`hsLcIFa+U z+oIt-FSr9!hHe2(r~1g)0{II7egs<^UU+D#QHxT58-%Kt!^WJN*jA!cd4gdnny7ne z`KiZ4vXHve^7AkJaG%F-)$I}!InruN+lL}gU93CpEIQL6RcCkAXkNGG+P^;JSX{MR z`~~P0^y*0o2@L=1_t4_p@yHG1@1!N+4#=7k{N)A@PRe{okaK+AYL!>4OW@0j+Pk{Y zFfQ56#t)d*xMjn8X3L+h+#LSLzyAmw>;*!DpI>2@t8#$A|L1zL>j2%mP4Y&={<+t505t{7u^7R7kEQS<$@}HV8~k1nv%3-ple1jU5{Qn2T>pAFdR`vzNQK zofR{qmAmBCmlQFMJA7KC5p#0LA(tr9Yg&ITz0|Nw57MySs%O^3J`Yk!g#$8F*4EWO zCp}211hBnz;D|PLxi@Qddvm49{W$(BA4xR7fhjeBV#5xw5puw9h)rFu2&s7iM0xC7DsdKPIh zevp^ePw^7(`|hH%=eB(B*QLs9wzDDH%|$aWadsJ~TLJGN37YA{qQJPO@Q_^kEx4c7>BL_?n_AIU_Ajph!NKc&J3olFj7yFQRHe9Mh!dq~ z@5Y)*4U-c0n%h1>e6{yN#)C%D&bo3WK=xBpsQJ5yB!cco`48oYA5v6zOAbX25V0@6 zd6mq9?J%Ur*QLX}jMnt1PBkQhy|9{6Y__8&jv=Qpn$2_zO-_OX^S0AZ-+kIW`<1Fv z!J~8bkoLRD8^>}Hm9|GH)n>U;^Y#L~u=)%IkmxY8rb0kK2QC7dI_8mkPvZMC&R@~k1p`d8U8k_@O(D3QrJ&sGIsb)BoNb1Njj5xSTyXi ze1y?_yqwhM{CWmoBI#>QX_hS&dE`N+?{6bABg|T)Q z?RL91kskJ_E(4=X;LTQ!i7h0mI6>Jeg__uJx`u2$$*t8{cJV}&Wg%esavIc;#(E_{ zl=a#}lcIat0Xc!b=3|r^8Q9vW-5Hnl`AqK@$vuN)Q_S2qMGu|ETsq D4~mH? literal 2829 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 handler = + new JsonBodyHandler<>(SignResponse.class, ErrorResponse.class, objectMapper); + + final HttpResponse.ResponseInfo info = responseInfo(200, "application/json; charset=utf-8"); + + final String json = """ + { + "orderRef":"131daac9-16c6-4618-beb0-365768f37288", + "autoStartToken":"7c40b5c9-fa74-49cf-b98c-bfe651f9a7c6" + }"""; + + final HttpResponse.BodySubscriber subscriber = handler.apply(info); + final SignResponse result = complete(feed(subscriber, json)); + + Assertions.assertNotNull(result); + Assertions.assertEquals("131daac9-16c6-4618-beb0-365768f37288", result.getOrderRef()); + Assertions.assertEquals("7c40b5c9-fa74-49cf-b98c-bfe651f9a7c6", result.getAutoStartToken()); + } + + @Test + void givenStatus200AndInvalidJson_whenApply_thenThrowsUnexpectedResponse() { + final JsonBodyHandler handler = + new JsonBodyHandler<>(SignResponse.class, ErrorResponse.class, objectMapper); + final HttpResponse.ResponseInfo info = responseInfo(200, "application/json"); + final String invalidJson = "not-a-json"; + + final HttpResponse.BodySubscriber subscriber = handler.apply(info); + + final BankIdApiUnexpectedResponseException ex = Assertions.assertThrows( + BankIdApiUnexpectedResponseException.class, + () -> complete(feed(subscriber, invalidJson)) + ); + Assertions.assertTrue(ex.getMessage() != null && !ex.getMessage().isBlank()); + } + + @Test + void givenNon200AndMissingContentType_whenApply_thenThrowsUnexpectedResponse() { + final JsonBodyHandler handler = + new JsonBodyHandler<>(SignResponse.class, ErrorResponse.class, objectMapper); + final HttpResponse.ResponseInfo info = responseInfo(500, null); + final String body = "Server error"; + + final HttpResponse.BodySubscriber subscriber = handler.apply(info); + + Assertions.assertThrows(BankIdApiUnexpectedResponseException.class, () -> complete(feed(subscriber, body))); + } + + @Test + void givenNon200AndNonJsonContentType_whenApply_thenThrowsUnexpectedResponse() { + final JsonBodyHandler handler = + new JsonBodyHandler<>(SignResponse.class, ErrorResponse.class, objectMapper); + final HttpResponse.ResponseInfo info = responseInfo(404, "text/plain"); + final String body = "Not found"; + + final HttpResponse.BodySubscriber subscriber = handler.apply(info); + + Assertions.assertThrows(BankIdApiUnexpectedResponseException.class, () -> complete(feed(subscriber, body))); + } + + @Test + void givenNon200AndJsonContentTypeWithValidJson_whenApply_thenThrowsApiError() { + final JsonBodyHandler handler = + new JsonBodyHandler<>(SignResponse.class, ErrorResponse.class, objectMapper); + final HttpResponse.ResponseInfo info = responseInfo(400, "application/json"); + final String errorJson = "{\"errorCode\":\"BAD_REQUEST\",\"details\":\"Invalid input\"}"; + + final HttpResponse.BodySubscriber subscriber = handler.apply(info); + + Assertions.assertThrows(BankIdApiErrorException.class, () -> complete(feed(subscriber, errorJson))); + } + + @Test + void givenNon200AndJsonContentTypeWithInvalidJson_whenApply_thenThrowsUnexpectedResponse() { + final JsonBodyHandler handler = + new JsonBodyHandler<>(SignResponse.class, ErrorResponse.class, objectMapper); + final HttpResponse.ResponseInfo info = responseInfo(500, "Application/JSON; charset=UTF-8"); // case-insensitive + final String invalidJson = "{ this is broken }"; + + final HttpResponse.BodySubscriber subscriber = handler.apply(info); + + Assertions.assertThrows(BankIdApiUnexpectedResponseException.class, () -> complete(feed(subscriber, invalidJson))); + } + + private static HttpResponse.ResponseInfo responseInfo(final int status, final String contentType) { + final Map> headerMap = + contentType == null + ? Map.of() + : Map.of("Content-Type", List.of(contentType)); + + final HttpHeaders headers = HttpHeaders.of(headerMap, (k, v) -> true); + + return new HttpResponse.ResponseInfo() { + @Override + public int statusCode() { + return status; + } + + @Override + public HttpHeaders headers() { + return headers; + } + + @Override + public HttpClient.Version version() { + return HttpClient.Version.HTTP_1_1; + } + }; + } + + private static T complete(final CompletableFuture future) { + try { + return future.get(2, TimeUnit.SECONDS); + } catch (final TimeoutException e) { + fail("Timed out waiting for body subscriber to complete"); + return null; // unreachable + } catch (final ExecutionException e) { + if (e.getCause() instanceof RuntimeException re) { + throw re; + } + throw new CompletionException(e.getCause()); + } catch (final InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + } + + private static CompletableFuture feed(final HttpResponse.BodySubscriber subscriber, final String body) { + subscriber.onSubscribe(new NoopSubscription()); + subscriber.onNext(List.of(ByteBuffer.wrap(body.getBytes(StandardCharsets.UTF_8)))); + subscriber.onComplete(); + return subscriber.getBody().toCompletableFuture(); + } + + private static class NoopSubscription implements java.util.concurrent.Flow.Subscription { + @Override + public void request(long n) { /* no-op */ } + + @Override + public void cancel() { /* no-op */ } + } +} From 88678ba65a2fb694b239d5c7aaa2b010d439632e Mon Sep 17 00:00:00 2001 From: Nicklas Wallgren Date: Sun, 10 Aug 2025 17:49:55 +0200 Subject: [PATCH 2/2] fix(checkstyle): resolve remarks in JsonBodyHandlerUnitTest --- .../bankid/internal/http/JsonBodyHandlerUnitTest.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/test/java/dev/eidentification/bankid/internal/http/JsonBodyHandlerUnitTest.java b/src/test/java/dev/eidentification/bankid/internal/http/JsonBodyHandlerUnitTest.java index 793943c..ed42d81 100644 --- a/src/test/java/dev/eidentification/bankid/internal/http/JsonBodyHandlerUnitTest.java +++ b/src/test/java/dev/eidentification/bankid/internal/http/JsonBodyHandlerUnitTest.java @@ -164,9 +164,13 @@ private static CompletableFuture feed(final HttpResponse.BodySubscriber