From b7712adec4b4523e4ae1627d939ede5dbb0779d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Mon, 25 May 2026 16:26:50 +0200 Subject: [PATCH 1/3] return claims on OIDC validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../oidc/ZaasOidcValidationResult.java | 3 ++ .../service/internal/ZaasJwtService.java | 13 +++++- .../zaasclient/util/SimpleHttpResponse.java | 7 ++-- .../service/internal/ZaasClientTest.java | 9 ++-- .../service/internal/ZaasJwtServiceTest.java | 32 +++++++++----- .../zaas/controllers/AuthController.java | 23 ++++------ .../zaas/controllers/AuthControllerTest.java | 42 +++++++++---------- 7 files changed, 71 insertions(+), 58 deletions(-) diff --git a/zaas-client/src/main/java/org/zowe/apiml/zaasclient/oidc/ZaasOidcValidationResult.java b/zaas-client/src/main/java/org/zowe/apiml/zaasclient/oidc/ZaasOidcValidationResult.java index 7399ee51ba..5842dd7ef0 100644 --- a/zaas-client/src/main/java/org/zowe/apiml/zaasclient/oidc/ZaasOidcValidationResult.java +++ b/zaas-client/src/main/java/org/zowe/apiml/zaasclient/oidc/ZaasOidcValidationResult.java @@ -13,6 +13,8 @@ import lombok.AllArgsConstructor; import lombok.Data; +import java.util.Map; + /** * */ @@ -21,5 +23,6 @@ public class ZaasOidcValidationResult { private boolean isValid = false; + private Map claims; } diff --git a/zaas-client/src/main/java/org/zowe/apiml/zaasclient/service/internal/ZaasJwtService.java b/zaas-client/src/main/java/org/zowe/apiml/zaasclient/service/internal/ZaasJwtService.java index bd8ca7679d..035315048c 100644 --- a/zaas-client/src/main/java/org/zowe/apiml/zaasclient/service/internal/ZaasJwtService.java +++ b/zaas-client/src/main/java/org/zowe/apiml/zaasclient/service/internal/ZaasJwtService.java @@ -42,6 +42,7 @@ import java.net.HttpCookie; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -166,12 +167,20 @@ private ClassicHttpRequest validateOidcToken(String oidcToken) throws JsonProces private ZaasOidcValidationResult extractZaasOidcValidate(SimpleHttpResponse response) throws IOException, ZaasClientException { int statusCode = response.getCode(); + + if (statusCode == 200) { + var body = response.getStringBody(); + var mapper = new ObjectMapper(); + var claims = mapper.readValue(body, Map.class); + return new ZaasOidcValidationResult(true, claims); + } + if (statusCode == 204) { - return new ZaasOidcValidationResult(true); + return new ZaasOidcValidationResult(true, null); } if (statusCode == 401) { - return new ZaasOidcValidationResult(false); + return new ZaasOidcValidationResult(false, null); } else { throw new ZaasClientException(ZaasClientErrorCodes.GENERIC_EXCEPTION, response.getStringBody()); } diff --git a/zaas-client/src/main/java/org/zowe/apiml/zaasclient/util/SimpleHttpResponse.java b/zaas-client/src/main/java/org/zowe/apiml/zaasclient/util/SimpleHttpResponse.java index 0b1f4cf8db..46eb0b60e3 100644 --- a/zaas-client/src/main/java/org/zowe/apiml/zaasclient/util/SimpleHttpResponse.java +++ b/zaas-client/src/main/java/org/zowe/apiml/zaasclient/util/SimpleHttpResponse.java @@ -19,6 +19,7 @@ import org.apache.hc.core5.http.io.entity.EntityUtils; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; @@ -44,15 +45,15 @@ public class SimpleHttpResponse { private final Map> headers; public SimpleHttpResponse(int code, byte[] byteBody) { - this(code, byteBody, null, Map.of()); + this(code, byteBody, new String(byteBody), Map.of()); } public SimpleHttpResponse(int code, String stringBody) { - this(code, null, stringBody, Map.of()); + this(code, stringBody.getBytes(StandardCharsets.UTF_8), stringBody, Map.of()); } public SimpleHttpResponse(int code, String stringBody, Map> headers) { - this(code, null, stringBody, headers); + this(code, stringBody.getBytes(StandardCharsets.UTF_8), stringBody, headers); } public SimpleHttpResponse(int code) { diff --git a/zaas-client/src/test/java/org/zowe/apiml/zaasclient/service/internal/ZaasClientTest.java b/zaas-client/src/test/java/org/zowe/apiml/zaasclient/service/internal/ZaasClientTest.java index 6ddf91d314..fd8150e784 100644 --- a/zaas-client/src/test/java/org/zowe/apiml/zaasclient/service/internal/ZaasClientTest.java +++ b/zaas-client/src/test/java/org/zowe/apiml/zaasclient/service/internal/ZaasClientTest.java @@ -40,10 +40,7 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; import static org.zowe.apiml.zaasclient.exception.ZaasClientErrorCodes.*; @@ -234,7 +231,7 @@ void createConfigProperties() { @Test void givenValidOidcToken_whenValidate_thenSuccess() throws ZaasClientException { var token = "validOidcToken"; - var successResult = new ZaasOidcValidationResult(true); + var successResult = new ZaasOidcValidationResult(true, null); when(tokens.validateOidc(token)).thenReturn(successResult); assertSame(successResult, underTest.validateOidc(token)); } @@ -252,7 +249,7 @@ void givenValidOidc_whenValidateException_thenRethrown() throws ZaasClientExcept @Test void givenInvalidOidc_whenValidate_thenFalse() throws ZaasClientException { var token = "invalidOidc"; - var failResult = new ZaasOidcValidationResult(false); + var failResult = new ZaasOidcValidationResult(false, null); when(tokens.validateOidc(token)).thenReturn(failResult); assertSame(failResult, underTest.validateOidc(token)); } diff --git a/zaas-client/src/test/java/org/zowe/apiml/zaasclient/service/internal/ZaasJwtServiceTest.java b/zaas-client/src/test/java/org/zowe/apiml/zaasclient/service/internal/ZaasJwtServiceTest.java index 07a5d3987b..ba2adbbb48 100644 --- a/zaas-client/src/test/java/org/zowe/apiml/zaasclient/service/internal/ZaasJwtServiceTest.java +++ b/zaas-client/src/test/java/org/zowe/apiml/zaasclient/service/internal/ZaasJwtServiceTest.java @@ -38,16 +38,9 @@ import java.io.IOException; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; import static org.zowe.apiml.zaasclient.exception.ZaasClientErrorCodes.GENERIC_EXCEPTION; import static org.zowe.apiml.zaasclient.exception.ZaasClientErrorCodes.TOKEN_NOT_PROVIDED; @@ -226,11 +219,30 @@ void givenEmptyOidcToken_whenValidate_thenException() { } @Test - void givenValidToken_whenValidate_thenSuccess() throws ZaasClientException { + void givenValidToken_whenValidateBefore3_6_0_thenSuccess() throws ZaasClientException { var token = "validOidcToken"; mockHttpClientResponse(204); var validationResult = zaasJwtService.validateOidc(token); assertTrue(validationResult.isValid()); + assertNull(validationResult.getClaims()); + } + + @Test + void givenValidToken_whenValidateOn3_6_0_orLaterVersion_thenSuccess() throws ZaasClientException { + var token = "validOidcToken"; + mockHttpClientResponse(200, """ + { + "sub": "user", + "anotherClaim": "x", + "exp": 5 + } + """); + var validationResult = zaasJwtService.validateOidc(token); + assertTrue(validationResult.isValid()); + assertNotNull(validationResult.getClaims()); + assertEquals("user", validationResult.getClaims().get("sub")); + assertEquals("x", validationResult.getClaims().get("anotherClaim")); + assertEquals(5, validationResult.getClaims().get("exp")); } @Test diff --git a/zaas-service/src/main/java/org/zowe/apiml/zaas/controllers/AuthController.java b/zaas-service/src/main/java/org/zowe/apiml/zaas/controllers/AuthController.java index dd4df09a00..0c42ca9869 100644 --- a/zaas-service/src/main/java/org/zowe/apiml/zaas/controllers/AuthController.java +++ b/zaas-service/src/main/java/org/zowe/apiml/zaas/controllers/AuthController.java @@ -38,19 +38,13 @@ import org.springframework.lang.Nullable; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import org.zowe.apiml.message.api.ApiMessageView; import org.zowe.apiml.message.core.MessageService; import org.zowe.apiml.security.common.token.AccessTokenProvider; import org.zowe.apiml.security.common.token.OIDCProvider; import org.zowe.apiml.security.common.token.TokenNotValidException; +import org.zowe.apiml.security.common.util.JwtUtils; import org.zowe.apiml.zaas.security.service.AuthenticationService; import org.zowe.apiml.zaas.security.service.JwtSecurity; import org.zowe.apiml.zaas.security.service.token.OIDCTokenProvider; @@ -66,9 +60,7 @@ import java.util.List; import java.util.Map; -import static org.apache.http.HttpStatus.SC_NO_CONTENT; -import static org.apache.http.HttpStatus.SC_OK; -import static org.apache.http.HttpStatus.SC_SERVICE_UNAVAILABLE; +import static org.apache.http.HttpStatus.*; /** * Controller offer method to control security. It can contain method for user and also method for calling services @@ -464,7 +456,7 @@ private List getCurrentKey() { return currentKey.getJsonWebKeys(); } - @PostMapping(path = OIDC_TOKEN_VALIDATE) + @PostMapping(path = OIDC_TOKEN_VALIDATE, produces = MediaType.APPLICATION_JSON_VALUE) @Operation(summary = "Validate OIDC token", tags = {"OIDC"}, operationId = "validateOIDCToken", @@ -481,13 +473,14 @@ private List getCurrentKey() { @ApiResponse(responseCode = "204", description = "Valid token"), @ApiResponse(responseCode = "401", description = "Invalid token or OIDC provider is not defined") }) - public ResponseEntity validateOIDCToken(@RequestBody ValidateRequestModel validateRequestModel) { + public ResponseEntity validateOIDCToken(@RequestBody ValidateRequestModel validateRequestModel) { log.debug("Validating OIDC token using provider {}", oidcProvider); String token = validateRequestModel.getToken(); if (oidcProvider != null && oidcProvider.isValid(token)) { - return new ResponseEntity<>(HttpStatus.NO_CONTENT); + return new ResponseEntity<>(JwtUtils.getJwtClaims(token).getClaims(), HttpStatus.OK); } - return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); + final ApiMessageView message = messageService.createMessage("org.zowe.apiml.common.unauthorized").mapToView(); + return new ResponseEntity<>(message, HttpStatus.UNAUTHORIZED); } /** diff --git a/zaas-service/src/test/java/org/zowe/apiml/zaas/controllers/AuthControllerTest.java b/zaas-service/src/test/java/org/zowe/apiml/zaas/controllers/AuthControllerTest.java index 0df7a67239..ffe4857166 100644 --- a/zaas-service/src/test/java/org/zowe/apiml/zaas/controllers/AuthControllerTest.java +++ b/zaas-service/src/test/java/org/zowe/apiml/zaas/controllers/AuthControllerTest.java @@ -50,25 +50,12 @@ import java.util.List; import java.util.Optional; -import static org.apache.http.HttpStatus.SC_BAD_REQUEST; -import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR; -import static org.apache.http.HttpStatus.SC_NOT_FOUND; -import static org.apache.http.HttpStatus.SC_NO_CONTENT; -import static org.apache.http.HttpStatus.SC_OK; -import static org.apache.http.HttpStatus.SC_SERVICE_UNAVAILABLE; -import static org.apache.http.HttpStatus.SC_UNAUTHORIZED; +import static org.apache.http.HttpStatus.*; import static org.hamcrest.CoreMatchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @ExtendWith(SpringExtension.class) class AuthControllerTest { @@ -416,17 +403,26 @@ void thenRemoveRulesAndTokens() throws Exception { @Nested class GivenValidateOIDCTokenRequest { - private static final String TOKEN = "token"; - @Nested class WhenValidateToken { + + private static final String TOKEN = "ewogICJ0eXAiOiAiSldUIiwKICAibm9uY2UiOiAiYVZhbHVlVG9CZVZlcmlmaWVkIiwKICAiYWxnIjogIlJTMjU2IiwKICAia2lkIjogIlNlQ1JldEtleSIKfQ.ewogICJhdWQiOiAiMDAwMDAwMDMtMDAwMC0wMDAwLWMwMDAtMDAwMDAwMDAwMDAwIiwKICAiaXNzIjogImh0dHBzOi8vb2lkYy5wcm92aWRlci5vcmcvYXBwIiwKICAiaWF0IjogMTcyMjUxNDEyOSwKICAibmJmIjogMTcyMjUxNDEyOSwKICAiZXhwIjogODcyMjUxODEyNSwKICAic3ViIjogIm9pZGMudXNlcm5hbWUiCn0.c29tZVNpZ25lZEhhc2hDb2Rl"; + + private String getBody() throws JSONException { + return new JSONObject() + .put("token", TOKEN) + .toString(); + } + @Test void validateOIDCToken() throws Exception { when(oidcProvider.isValid(TOKEN)).thenReturn(true); mockMvc.perform(post("/zaas/api/v1/auth/oidc-token/validate") .contentType(MediaType.APPLICATION_JSON) - .content(body.toString())) - .andExpect(status().is(SC_NO_CONTENT)); + .content(getBody())) + .andExpect(status().is(SC_OK)) + .andExpect(jsonPath("sub", is("oidc.username"))) + .andExpect(jsonPath("iss", is("https://oidc.provider.org/app"))); } @Test @@ -434,10 +430,12 @@ void return401() throws Exception { when(oidcProvider.isValid(TOKEN)).thenReturn(false); mockMvc.perform(post("/zaas/api/v1/auth/oidc-token/validate") .contentType(MediaType.APPLICATION_JSON) - .content(body.toString())) + .content(getBody())) .andExpect(status().is(SC_UNAUTHORIZED)); } + } + } @Nested From f5a7dd37eb9db0026cc69254cc2bb733e8e3eeeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Wed, 27 May 2026 16:58:16 +0200 Subject: [PATCH 2/3] fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../integration/authentication/oauth2/OidcOauth2Test.java | 2 +- .../org/zowe/apiml/zaasclient/util/SimpleHttpResponse.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/integration-tests/src/test/java/org/zowe/apiml/integration/authentication/oauth2/OidcOauth2Test.java b/integration-tests/src/test/java/org/zowe/apiml/integration/authentication/oauth2/OidcOauth2Test.java index eabd77c18d..a214649bd2 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/integration/authentication/oauth2/OidcOauth2Test.java +++ b/integration-tests/src/test/java/org/zowe/apiml/integration/authentication/oauth2/OidcOauth2Test.java @@ -113,7 +113,7 @@ void thenValidateReturns200(String validToken) { .body(requestBody) .when() .post(VALIDATE_ENDPOINT) - .then().statusCode(HttpStatus.SC_NO_CONTENT); + .then().statusCode(HttpStatus.SC_OK); } @Nested diff --git a/zaas-client/src/main/java/org/zowe/apiml/zaasclient/util/SimpleHttpResponse.java b/zaas-client/src/main/java/org/zowe/apiml/zaasclient/util/SimpleHttpResponse.java index 46eb0b60e3..ed6351068a 100644 --- a/zaas-client/src/main/java/org/zowe/apiml/zaasclient/util/SimpleHttpResponse.java +++ b/zaas-client/src/main/java/org/zowe/apiml/zaasclient/util/SimpleHttpResponse.java @@ -49,11 +49,11 @@ public SimpleHttpResponse(int code, byte[] byteBody) { } public SimpleHttpResponse(int code, String stringBody) { - this(code, stringBody.getBytes(StandardCharsets.UTF_8), stringBody, Map.of()); + this(code, stringBody == null ? null : stringBody.getBytes(StandardCharsets.UTF_8), stringBody, Map.of()); } public SimpleHttpResponse(int code, String stringBody, Map> headers) { - this(code, stringBody.getBytes(StandardCharsets.UTF_8), stringBody, headers); + this(code, stringBody == null ? null : stringBody.getBytes(StandardCharsets.UTF_8), stringBody, headers); } public SimpleHttpResponse(int code) { From aad1e187c94c46dccbb476bf1447e081498cb63b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Mon, 1 Jun 2026 16:39:19 +0200 Subject: [PATCH 3/3] code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../zowe/apiml/zaasclient/service/internal/ZaasJwtService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zaas-client/src/main/java/org/zowe/apiml/zaasclient/service/internal/ZaasJwtService.java b/zaas-client/src/main/java/org/zowe/apiml/zaasclient/service/internal/ZaasJwtService.java index 035315048c..79a5498fe3 100644 --- a/zaas-client/src/main/java/org/zowe/apiml/zaasclient/service/internal/ZaasJwtService.java +++ b/zaas-client/src/main/java/org/zowe/apiml/zaasclient/service/internal/ZaasJwtService.java @@ -170,8 +170,7 @@ private ZaasOidcValidationResult extractZaasOidcValidate(SimpleHttpResponse resp if (statusCode == 200) { var body = response.getStringBody(); - var mapper = new ObjectMapper(); - var claims = mapper.readValue(body, Map.class); + var claims = objectMapper.readValue(body, Map.class); return new ZaasOidcValidationResult(true, claims); }