From e7525d62dae925d31b5fe44cb22953021793e6fb Mon Sep 17 00:00:00 2001 From: strehle Date: Sat, 9 Aug 2025 18:30:28 +0100 Subject: [PATCH 1/5] remove jti from the required claims for jwt client authentication fix issue 3569 --- .../identity/uaa/oauth/jwt/JwtClientAuthentication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java index f841a673d10..7977336cedf 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java @@ -73,7 +73,7 @@ public class JwtClientAuthentication { // no signature check with invalid algorithms private static final Set NOT_SUPPORTED_ALGORITHMS = Set.of(Algorithm.NONE, JWSAlgorithm.HS256, JWSAlgorithm.HS384, JWSAlgorithm.HS512); private static final Set JWT_REQUIRED_CLAIMS = Set.of(ClaimConstants.ISS, ClaimConstants.SUB, ClaimConstants.AUD, - ClaimConstants.EXPIRY_IN_SECONDS, ClaimConstants.JTI); + ClaimConstants.EXPIRY_IN_SECONDS); private final KeyInfoService keyInfoService; private final OidcMetadataFetcher oidcMetadataFetcher; From 57cd0f38a4290ba1a93ee2502a19b26051e2f0c3 Mon Sep 17 00:00:00 2001 From: strehle Date: Sat, 27 Dec 2025 15:48:14 +0100 Subject: [PATCH 2/5] split required claims --- .../uaa/oauth/jwt/JwtClientAuthentication.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java index 7977336cedf..53294b673e5 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java @@ -72,8 +72,10 @@ public class JwtClientAuthentication { // no signature check with invalid algorithms private static final Set NOT_SUPPORTED_ALGORITHMS = Set.of(Algorithm.NONE, JWSAlgorithm.HS256, JWSAlgorithm.HS384, JWSAlgorithm.HS512); - private static final Set JWT_REQUIRED_CLAIMS = Set.of(ClaimConstants.ISS, ClaimConstants.SUB, ClaimConstants.AUD, + private static final Set JWT_RFC7523_CLAIMS = Set.of(ClaimConstants.ISS, ClaimConstants.SUB, ClaimConstants.AUD, ClaimConstants.EXPIRY_IN_SECONDS); + private static final Set JWT_OIDC_CLAIMS = Set.of(ClaimConstants.ISS, ClaimConstants.SUB, ClaimConstants.AUD, + ClaimConstants.EXPIRY_IN_SECONDS, ClaimConstants.JTI); private final KeyInfoService keyInfoService; private final OidcMetadataFetcher oidcMetadataFetcher; @@ -160,7 +162,7 @@ public boolean validateClientJwt(Map requestParameters, Client // Validate token according to private_key_jwt with OIDC return clientId.equals(validateClientJWToken(clientJWT, oidcMetadataFetcher == null ? new JWKSet() : JWKSet.parse(oidcMetadataFetcher.fetchWebKeySet(clientJwtConfiguration).getKeySetMap()), - clientId, clientId, keyInfoService.getTokenEndpointUrl()).getSubject()); + JWT_OIDC_CLAIMS, clientId, clientId, keyInfoService.getTokenEndpointUrl()).getSubject()); } else { // Check if we found trust for private_key_jwt with RFC 7523. We allow client_id (from request) != sub (client_assertion) ClientJwtCredential jwtFederation = getClientJwtFederation(clientJwtConfiguration, clientClaims); @@ -217,11 +219,12 @@ public static String getClientIdOidcAssertion(String clientAssertion) { } } + // Validate federated client with RFC 7523 private boolean validateFederatedClientWT(JWT jwtAssertion, JWTClaimsSet clientClaims, ClientJwtCredential jwtFederation) throws OidcMetadataFetchingException, ParseException { try { JWKSet jwkSet = retrieveJwkSet(clientClaims); String expectedAud = Optional.ofNullable(jwtFederation.getAudience()).orElse(keyInfoService.getTokenEndpointUrl()); - return validateClientJWToken(jwtAssertion, jwkSet, jwtFederation.getSubject(), jwtFederation.getIssuer(), expectedAud) != null; + return validateClientJWToken(jwtAssertion, jwkSet, JWT_RFC7523_CLAIMS, jwtFederation.getSubject(), jwtFederation.getIssuer(), expectedAud) != null; } catch (MalformedURLException | IllegalArgumentException | URISyntaxException e) { return false; } @@ -270,7 +273,7 @@ private JWKSet retrieveJwkSet(JWTClaimsSet clientClaims) throws MalformedURLExce } } - private JWTClaimsSet validateClientJWToken(JWT jwtAssertion, JWKSet jwkSet, String expectedSub, String expectIss, String expectedAud) { + private JWTClaimsSet validateClientJWToken(JWT jwtAssertion, JWKSet jwkSet, Set requiredClaims, String expectedSub, String expectIss, String expectedAud) { if (Optional.ofNullable(jwkSet).orElse(new JWKSet()).isEmpty()) { throw new BadCredentialsException("Bad empty jwk_set"); } @@ -284,7 +287,7 @@ private JWTClaimsSet validateClientJWToken(JWT jwtAssertion, JWKSet jwkSet, Stri jwtProcessor.setJWSKeySelector(keySelector); JWTClaimsSet.Builder claimSetBuilder = new JWTClaimsSet.Builder().issuer(expectIss).subject(expectedSub); - jwtProcessor.setJWTClaimsSetVerifier(new DefaultJWTClaimsVerifier<>(expectedAud, claimSetBuilder.build(), JWT_REQUIRED_CLAIMS)); + jwtProcessor.setJWTClaimsSetVerifier(new DefaultJWTClaimsVerifier<>(expectedAud, claimSetBuilder.build(), requiredClaims)); try { return jwtProcessor.process(jwtAssertion, null); From cff3a28ad00120abaae6faf89c1061efb6eab90e Mon Sep 17 00:00:00 2001 From: Markus Strehle <11627201+strehle@users.noreply.github.com> Date: Tue, 30 Dec 2025 18:58:28 +0100 Subject: [PATCH 3/5] Update server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../identity/uaa/oauth/jwt/JwtClientAuthentication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java index 53294b673e5..e981f64a0db 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java @@ -75,7 +75,7 @@ public class JwtClientAuthentication { private static final Set JWT_RFC7523_CLAIMS = Set.of(ClaimConstants.ISS, ClaimConstants.SUB, ClaimConstants.AUD, ClaimConstants.EXPIRY_IN_SECONDS); private static final Set JWT_OIDC_CLAIMS = Set.of(ClaimConstants.ISS, ClaimConstants.SUB, ClaimConstants.AUD, - ClaimConstants.EXPIRY_IN_SECONDS, ClaimConstants.JTI); + ClaimConstants.EXPIRY_IN_SECONDS, ClaimConstants.JTI); private final KeyInfoService keyInfoService; private final OidcMetadataFetcher oidcMetadataFetcher; From c2f87c9f18a4c0ad054d647c112ad7145184a5c0 Mon Sep 17 00:00:00 2001 From: Markus Strehle <11627201+strehle@users.noreply.github.com> Date: Wed, 31 Dec 2025 09:51:49 +0100 Subject: [PATCH 4/5] Update server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../identity/uaa/oauth/jwt/JwtClientAuthentication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java index e981f64a0db..0ff5e0519e8 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java @@ -220,7 +220,7 @@ public static String getClientIdOidcAssertion(String clientAssertion) { } // Validate federated client with RFC 7523 - private boolean validateFederatedClientWT(JWT jwtAssertion, JWTClaimsSet clientClaims, ClientJwtCredential jwtFederation) throws OidcMetadataFetchingException, ParseException { + private boolean validateFederatedClientJWT(JWT jwtAssertion, JWTClaimsSet clientClaims, ClientJwtCredential jwtFederation) throws OidcMetadataFetchingException, ParseException { try { JWKSet jwkSet = retrieveJwkSet(clientClaims); String expectedAud = Optional.ofNullable(jwtFederation.getAudience()).orElse(keyInfoService.getTokenEndpointUrl()); From 6c1981b9efe9d74b5fe52532b5e1884da9645c70 Mon Sep 17 00:00:00 2001 From: strehle Date: Wed, 31 Dec 2025 09:56:59 +0100 Subject: [PATCH 5/5] fix rename from copilot --- .../identity/uaa/oauth/jwt/JwtClientAuthentication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java index 0ff5e0519e8..399d93870f0 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthentication.java @@ -167,7 +167,7 @@ public boolean validateClientJwt(Map requestParameters, Client // Check if we found trust for private_key_jwt with RFC 7523. We allow client_id (from request) != sub (client_assertion) ClientJwtCredential jwtFederation = getClientJwtFederation(clientJwtConfiguration, clientClaims); if (jwtFederation != null) { - return validateFederatedClientWT(clientJWT, clientClaims, jwtFederation); + return validateFederatedClientJWT(clientJWT, clientClaims, jwtFederation); } throw new BadCredentialsException("Wrong client_assertion"); }