diff --git a/model/src/main/java/org/cloudfoundry/identity/uaa/util/UaaStringUtils.java b/model/src/main/java/org/cloudfoundry/identity/uaa/util/UaaStringUtils.java index 696f65a9f62..79b1ae3ba80 100644 --- a/model/src/main/java/org/cloudfoundry/identity/uaa/util/UaaStringUtils.java +++ b/model/src/main/java/org/cloudfoundry/identity/uaa/util/UaaStringUtils.java @@ -15,6 +15,7 @@ import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import java.net.MalformedURLException; @@ -317,4 +318,12 @@ public static String getSafeParameterValue(String[] value) { } return StringUtils.hasText(value[0]) ? value[0] : EMPTY_STRING; } + + public static Set getArrayDefaultValue(Set values, String defaultValue) { + if (ObjectUtils.isEmpty(values)) { + return Set.of(defaultValue); + } else { + return values; + } + } } diff --git a/model/src/test/java/org/cloudfoundry/identity/uaa/util/UaaStringUtilsTest.java b/model/src/test/java/org/cloudfoundry/identity/uaa/util/UaaStringUtilsTest.java index a6156ab68b3..4fc3c9789db 100644 --- a/model/src/test/java/org/cloudfoundry/identity/uaa/util/UaaStringUtilsTest.java +++ b/model/src/test/java/org/cloudfoundry/identity/uaa/util/UaaStringUtilsTest.java @@ -432,6 +432,13 @@ void getSafeParameterValue() { assertEquals("", UaaStringUtils.getSafeParameterValue(null)); } + @Test + void getArrayDefaultValue() { + assertEquals(Set.of("1", "2"), UaaStringUtils.getArrayDefaultValue(Set.of("1", "2"), "1")); + assertEquals(Set.of("1"), UaaStringUtils.getArrayDefaultValue(Set.of(), "1")); + assertEquals(Set.of("1"), UaaStringUtils.getArrayDefaultValue(null, "1")); + } + private static void replaceZoneVariables(IdentityZone zone) { String s = "https://{zone.subdomain}.domain.com/z/{zone.id}?id={zone.id}&domain={zone.subdomain}"; String expect = String.format("https://%s.domain.com/z/%s?id=%s&domain=%s", zone.getSubdomain(), zone.getId(), zone.getId(), zone.getSubdomain()); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServices.java b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServices.java index 79d51c2f814..af396720a26 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServices.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServices.java @@ -41,6 +41,7 @@ import org.cloudfoundry.identity.uaa.util.TimeService; import org.cloudfoundry.identity.uaa.util.JwtTokenSignedByThisUAA; import org.cloudfoundry.identity.uaa.util.UaaSecurityContextUtils; +import org.cloudfoundry.identity.uaa.util.UaaStringUtils; import org.cloudfoundry.identity.uaa.util.UaaTokenUtils; import org.cloudfoundry.identity.uaa.zone.MultitenantClientServices; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; @@ -550,7 +551,7 @@ private KeyInfo getActiveKeyInfo() { claims.put(ZONE_ID,IdentityZoneHolder.get().getId()); } - claims.put(AUD, resourceIds); + claims.put(AUD, UaaStringUtils.getArrayDefaultValue(resourceIds, clientId)); for (String excludedClaim : getExcludedClaims()) { claims.remove(excludedClaim); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/HeaderParameters.java b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/HeaderParameters.java index b954c4dba00..5e6957c8d18 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/HeaderParameters.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/HeaderParameters.java @@ -30,13 +30,13 @@ public class HeaderParameters { String cty; @JsonProperty @JsonInclude(Include.NON_NULL) - String jwk; + Object jwk; @JsonProperty @JsonInclude(Include.NON_NULL) String x5u; @JsonProperty @JsonInclude(Include.NON_NULL) - String x5c; + Object x5c; @JsonProperty @JsonInclude(Include.NON_NULL) String x5t; diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/refresh/RefreshTokenCreator.java b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/refresh/RefreshTokenCreator.java index 583dbd2beb0..37da0a31463 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/refresh/RefreshTokenCreator.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/refresh/RefreshTokenCreator.java @@ -9,6 +9,7 @@ import org.cloudfoundry.identity.uaa.util.JsonUtils; import org.cloudfoundry.identity.uaa.util.TimeService; import org.cloudfoundry.identity.uaa.util.JwtTokenSignedByThisUAA; +import org.cloudfoundry.identity.uaa.util.UaaStringUtils; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.cloudfoundry.identity.uaa.zone.TokenPolicy; import org.springframework.security.authentication.InternalAuthenticationServiceException; @@ -88,7 +89,7 @@ private String buildJwtToken(UaaUser user, claims.put(CLIENT_ID, tokenRequestData.clientId); claims.put(ISS, tokenEndpointBuilder.getTokenEndpoint(IdentityZoneHolder.get())); claims.put(ZONE_ID, IdentityZoneHolder.get().getId()); - claims.put(AUD, tokenRequestData.resourceIds); + claims.put(AUD, UaaStringUtils.getArrayDefaultValue(tokenRequestData.resourceIds, tokenRequestData.clientId)); claims.put(GRANTED_SCOPES, tokenRequestData.scopes); if (null != tokenRequestData.authenticationMethods && !tokenRequestData.authenticationMethods.isEmpty()) { diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/TokenTestSupport.java b/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/TokenTestSupport.java index d69c3614584..992d04dc418 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/TokenTestSupport.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/TokenTestSupport.java @@ -191,8 +191,8 @@ public TokenTestSupport(UaaTokenEnhancer tokenEnhancer, KeyInfoService keyInfo) IdentityZoneConfiguration config = new IdentityZoneConfiguration(); tokenPolicy = new TokenPolicy(accessTokenValidity, refreshTokenValidity); Map keys = new HashMap<>(); - keys.put("testKey", "9c247h8yt978w3nv45y978w45hntv6"); - keys.put("otherKey", "unc0uf98gv89egh4v98749978hv"); + keys.put("testKey", "9c247h8yt978w3nv45y978w45hntv6210"); + keys.put("otherKey", "unc0uf98gv89egh4v98749978hvy52oa"); tokenPolicy.setKeys(keys); tokenPolicy.setActiveKeyId("testKey"); config.setTokenPolicy(tokenPolicy); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManagerIT.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManagerIT.java index 53a86c95fbf..924a9de50e5 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManagerIT.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManagerIT.java @@ -187,7 +187,7 @@ void setUp() throws Exception { IdentityZoneHolder.clear(); String keyName = "testKey"; header = map( - entry("alg", "HS256"), + entry("alg", "RS256"), entry("kid", keyName), entry("typ", "JWT") ); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManagerTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManagerTest.java index 23c8374d61d..3b2085d7950 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManagerTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManagerTest.java @@ -159,11 +159,14 @@ public void getExternalAuthenticationDetails_whenProviderHasSigningKey_throwsWhe expectedException.expectMessage("Could not verify token signature."); Map header = map( - entry(HeaderParameterNames.ALGORITHM, JWSAlgorithm.HS256.getName()), + entry(HeaderParameterNames.ALGORITHM, JWSAlgorithm.RS256.getName()), entry(HeaderParameterNames.KEY_ID, OIDC_PROVIDER_KEY) ); Signer signer = new RsaSigner(changedOidcProviderTokenSigningKey); Map claims = map( + entry(EXPIRY_IN_SECONDS, 0), + entry(AUD, "uaa-relying-party"), + entry(ISS, oidcConfig.getIssuer()), entry(EMAIL, "someuser@google.com") ); IdentityZoneHolder.get().getConfig().getTokenPolicy().setKeys(Collections.singletonMap("uaa-key", uaaIdentityZoneTokenSigningKey)); @@ -180,7 +183,7 @@ public void getExternalAuthenticationDetails_whenProviderIssuerMatchesUaaIssuer_ expectedException.expectMessage("Could not verify token signature."); Map header = map( - entry(HeaderParameterNames.ALGORITHM, JWSAlgorithm.HS256.getName()), + entry(HeaderParameterNames.ALGORITHM, JWSAlgorithm.RS256.getName()), entry(HeaderParameterNames.KEY_ID, "uaa-key") ); Signer signer = new RsaSigner(oidcProviderTokenSigningKey); @@ -197,7 +200,7 @@ public void getExternalAuthenticationDetails_whenProviderIssuerMatchesUaaIssuer_ @Test public void getExternalAuthenticationDetails_doesNotThrowWhenIdTokenIsValid() { Map header = map( - entry(HeaderParameterNames.ALGORITHM, JWSAlgorithm.HS256.getName()), + entry(HeaderParameterNames.ALGORITHM, JWSAlgorithm.RS256.getName()), entry(HeaderParameterNames.KEY_ID, OIDC_PROVIDER_KEY) ); Signer signer = new RsaSigner(oidcProviderTokenSigningKey); @@ -220,7 +223,7 @@ public void getExternalAuthenticationDetails_doesNotThrowWhenIdTokenIsValid() { public void getExternalAuthenticationDetails_whenUaaToken_doesNotThrowWhenIdTokenIsValid() { oidcConfig.setIssuer(tokenEndpointBuilder.getTokenEndpoint(IdentityZoneHolder.get())); Map header = map( - entry(HeaderParameterNames.ALGORITHM, JWSAlgorithm.HS256.getName()), + entry(HeaderParameterNames.ALGORITHM, JWSAlgorithm.RS256.getName()), entry(HeaderParameterNames.KEY_ID, "uaa-key") ); Signer signer = new RsaSigner(uaaIdentityZoneTokenSigningKey); @@ -243,7 +246,7 @@ public void getExternalAuthenticationDetails_whenUaaToken_doesNotThrowWhenIdToke public void getExternalAuthenticationDetails_whenUaaToken_mapRoleAsExplicitToScopeWhenIdTokenIsValid() { oidcConfig.setIssuer(tokenEndpointBuilder.getTokenEndpoint(IdentityZoneHolder.get())); Map header = map( - entry(HeaderParameterNames.ALGORITHM, JWSAlgorithm.HS256.getName()), + entry(HeaderParameterNames.ALGORITHM, JWSAlgorithm.RS256.getName()), entry(HeaderParameterNames.KEY_ID, "uaa-key") ); Signer signer = new RsaSigner(uaaIdentityZoneTokenSigningKey); @@ -274,7 +277,7 @@ public void getExternalAuthenticationDetails_whenUaaToken_mapRoleAsExplicitToSco public void getExternalAuthenticationDetails_whenUaaToken_mapRoleAsScopeToScopeWhenIdTokenIsValid() { oidcConfig.setIssuer(tokenEndpointBuilder.getTokenEndpoint(IdentityZoneHolder.get())); Map header = map( - entry(HeaderParameterNames.ALGORITHM, JWSAlgorithm.HS256.getName()), + entry(HeaderParameterNames.ALGORITHM, JWSAlgorithm.RS256.getName()), entry(HeaderParameterNames.KEY_ID, "uaa-key") ); Signer signer = new RsaSigner(uaaIdentityZoneTokenSigningKey); @@ -307,7 +310,7 @@ public void getExternalAuthenticationDetails_whenUaaToken_mapRoleAsScopeToScopeW public void getExternalAuthenticationDetails_whenUaaToken_mapRoleAsScopeToScopeWhenIdTokenIsValid_AndFilterManagerRolesOnly() { oidcConfig.setIssuer(tokenEndpointBuilder.getTokenEndpoint(IdentityZoneHolder.get())); Map header = map( - entry(HeaderParameterNames.ALGORITHM, JWSAlgorithm.HS256.getName()), + entry(HeaderParameterNames.ALGORITHM, JWSAlgorithm.RS256.getName()), entry(HeaderParameterNames.KEY_ID, "uaa-key") ); Signer signer = new RsaSigner(uaaIdentityZoneTokenSigningKey); @@ -339,7 +342,7 @@ public void getExternalAuthenticationDetails_whenUaaToken_mapRoleAsScopeToScopeW @Test public void getUser_doesNotThrowWhenIdTokenMappingIsArray() { Map header = map( - entry(HeaderParameterNames.ALGORITHM, JWSAlgorithm.HS256.getName()), + entry(HeaderParameterNames.ALGORITHM, JWSAlgorithm.RS256.getName()), entry(HeaderParameterNames.KEY_ID, OIDC_PROVIDER_KEY) ); Signer signer = new RsaSigner(oidcProviderTokenSigningKey); @@ -374,7 +377,7 @@ public void getUser_doesNotThrowWhenIdTokenMappingIsArray() { @Test public void getUser_doesThrowWhenIdTokenMappingIsAmbiguous() { Map header = map( - entry(HeaderParameterNames.ALGORITHM, JWSAlgorithm.HS256.getName()), + entry(HeaderParameterNames.ALGORITHM, JWSAlgorithm.RS256.getName()), entry(HeaderParameterNames.KEY_ID, OIDC_PROVIDER_KEY) ); Signer signer = new RsaSigner(oidcProviderTokenSigningKey); @@ -402,7 +405,7 @@ public void getUser_doesThrowWhenIdTokenMappingIsAmbiguous() { @Test public void getUser_doesThrowWhenIdTokenMappingIsWrongType() { Map header = map( - entry(HeaderParameterNames.ALGORITHM, JWSAlgorithm.HS256.getName()), + entry(HeaderParameterNames.ALGORITHM, JWSAlgorithm.RS256.getName()), entry(HeaderParameterNames.KEY_ID, OIDC_PROVIDER_KEY) ); Signer signer = new RsaSigner(oidcProviderTokenSigningKey); diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenMvcMockTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenMvcMockTests.java index 2b20664708f..77730ceb5e2 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenMvcMockTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenMvcMockTests.java @@ -100,6 +100,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.GregorianCalendar; @@ -4148,7 +4149,13 @@ private void validateOpenIdConnectToken(String token, String userId, String clie assertEquals(tokenEndpointBuilder.getTokenEndpoint(IdentityZoneHolder.get()), iss); String sub = (String) result.get(ClaimConstants.SUB); assertEquals(userId, sub); - List aud = (List) result.get(ClaimConstants.AUD); + Object audObject = result.get(ClaimConstants.AUD); + List aud = new ArrayList<>(); + if (audObject instanceof Collection) { + aud.addAll((List) result.get(ClaimConstants.AUD)); + } else if (audObject instanceof String audString) { + aud.add(audString); + } assertTrue(aud.contains(clientId)); Integer exp = (Integer) result.get(ClaimConstants.EXPIRY_IN_SECONDS); assertNotNull(exp);