From 4c038e97dd20180072a4b72b9366e26b4fef407d Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Mon, 2 Sep 2024 19:02:57 +0100 Subject: [PATCH] Support OIDC signed UserInfo with charset content type parameters --- .../io/quarkus/oidc/runtime/OidcProvider.java | 17 ++++++++++++++++- .../quarkus/oidc/runtime/OidcProviderTest.java | 12 ++++++++++++ .../it/keycloak/CodeFlowAuthorizationTest.java | 2 +- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProvider.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProvider.java index 0aa05ccaa0c5d..c315289615ffb 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProvider.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProvider.java @@ -415,7 +415,7 @@ public Uni getUserInfo(String accessToken) { @Override public Uni apply(UserInfoResponse response) { - if (APPLICATION_JWT_CONTENT_TYPE.equals(response.contentType())) { + if (isApplicationJwtContentType(response.contentType())) { if (oidcConfig.jwks.resolveEarly) { try { LOG.debugf("Verifying the signed UserInfo with the local JWK keys: %s", response.data()); @@ -446,6 +446,21 @@ public Uni apply(UserInfoResponse response) { }); } + static boolean isApplicationJwtContentType(String ct) { + if (ct == null) { + return false; + } + ct = ct.trim(); + if (!ct.startsWith(APPLICATION_JWT_CONTENT_TYPE)) { + return false; + } + if (ct.length() == APPLICATION_JWT_CONTENT_TYPE.length()) { + return true; + } + String remainder = ct.substring(APPLICATION_JWT_CONTENT_TYPE.length()).trim(); + return remainder.indexOf(';') == 0; + } + public Uni getCodeFlowTokens(String code, String redirectUri, String codeVerifier) { return client.getAuthorizationCodeTokens(code, redirectUri, codeVerifier); } diff --git a/extensions/oidc/runtime/src/test/java/io/quarkus/oidc/runtime/OidcProviderTest.java b/extensions/oidc/runtime/src/test/java/io/quarkus/oidc/runtime/OidcProviderTest.java index 773e8f8a01ac9..9b874b1516f08 100644 --- a/extensions/oidc/runtime/src/test/java/io/quarkus/oidc/runtime/OidcProviderTest.java +++ b/extensions/oidc/runtime/src/test/java/io/quarkus/oidc/runtime/OidcProviderTest.java @@ -1,6 +1,7 @@ package io.quarkus.oidc.runtime; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -310,4 +311,15 @@ public String validate(JwtContext jwtContext) throws MalformedClaimException { } } } + + @Test + public void testJwtContentTypeCheck() { + assertTrue(OidcProvider.isApplicationJwtContentType("application/jwt")); + assertTrue(OidcProvider.isApplicationJwtContentType(" application/jwt ")); + assertTrue(OidcProvider.isApplicationJwtContentType("application/jwt;charset=UTF-8")); + assertTrue(OidcProvider.isApplicationJwtContentType(" application/jwt ; charset=UTF-8")); + assertFalse(OidcProvider.isApplicationJwtContentType(" application/jwt-custom")); + assertFalse(OidcProvider.isApplicationJwtContentType(" application/json")); + assertFalse(OidcProvider.isApplicationJwtContentType(null)); + } } diff --git a/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/CodeFlowAuthorizationTest.java b/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/CodeFlowAuthorizationTest.java index b8a747756eea8..834b96d7030c1 100644 --- a/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/CodeFlowAuthorizationTest.java +++ b/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/CodeFlowAuthorizationTest.java @@ -617,7 +617,7 @@ private void defineCodeFlowUserInfoCachedInIdTokenStub() { get(urlEqualTo("/auth/realms/quarkus/protocol/openid-connect/signeduserinfo")) .withHeader("Authorization", containing("Bearer ey")) .willReturn(aResponse() - .withHeader("Content-Type", "application/jwt") + .withHeader("Content-Type", " application/jwt ; charset=UTF-8") .withBody( Jwt.preferredUserName("alice") .issuer("https://server.example.com")