From 358b9f38ca46861bc24068f90aacc71a26559187 Mon Sep 17 00:00:00 2001 From: strehle Date: Thu, 28 Sep 2023 15:28:40 +0200 Subject: [PATCH] test: Add Client Authentication Integration Tests Run different grant types with private_key_jwt authentication instead of client_secret Works after merge of https://github.com/cloudfoundry/uaa/pull/2507 --- .../oauth/jwt/JwtClientAuthentication.java | 2 + .../resources/integration.test.properties | 3 +- .../webapp/WEB-INF/spring/oauth-clients.xml | 23 ++ .../feature/PrivateKeyJwtClientAuthIT.java | 362 ++++++++++++++++++ .../uaa/integration/feature/TestClient.java | 20 + .../util/IntegrationTestUtils.java | 48 ++- 6 files changed, 455 insertions(+), 3 deletions(-) create mode 100644 uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/PrivateKeyJwtClientAuthIT.java 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 3377477d8eb..60cc5fe4c6d 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 @@ -18,6 +18,8 @@ public class JwtClientAuthentication { public static final String GRANT_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"; + public static final String CLIENT_ASSERTION = "client_assertion"; + public static final String CLIENT_ASSERTION_TYPE = "client_assertion_type"; private final KeyInfoService keyInfoService; diff --git a/server/src/test/resources/integration.test.properties b/server/src/test/resources/integration.test.properties index 1fd54dd5cfd..560a134146d 100644 --- a/server/src/test/resources/integration.test.properties +++ b/server/src/test/resources/integration.test.properties @@ -1,4 +1,5 @@ integration.test.app_url=http://localhost:8080/app/ integration.test.base_url=http://localhost:8080/uaa integration.test.timeout_multiplier=1 -smtp.port=2525 \ No newline at end of file +smtp.port=2525 +integration.test.signing-key=-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCowKUlfOfJxXZtDWkVs3xb4BZJiGlLYxUAaGRY2WbG/YjHT/6frOOK+N2jFyrtElHiRXJyhV4PTsOJYSVhKdAt15A+AoBwGLCKVHfRTLINMpyoNBDmuQKDY42XBXRoyyDvgppd5exXrncBKzcVgS25LVoP8Nvn4XJcXweQejzHLX01SeqwNZCeHUeGSXKfG7a29bR/DagMTWnA2X5YsRU+2VykK1/hVK/4ZrC0GIjrGZiwYEwL3Db0RIcWo/DQ1IJGGXIl/qsME0f/vrqbMr+8TMDivMZMERSoPFOD/wmlGGH0PeqWNKyaoK2lCiWg4BpQoKpIlv+Eo+yl77uv1xibAgMBAAECggEADtC4jwJ/MDuZ6pGtvKSBEgKp7wzyJZa00ZzYo0sVSi1L58FSDiW15Zqn84YSR2iY1l//eY0HVYCDC6aDC07W9cQoaArjLzQ6GslQqm6GOtqX+CJ3q2Uc+RKkuL7XWgEfZDexb4+PwNQfb/OIOgCZCY1kP0sHm3BNEIDQheXD1gtq8KTOBy/TtN7rV940LoudgQ8vzz+ShhmG7Dt5yws/QzaBpryLncGsGYZSDnvvEBYYdlbYQEgfLmzdKSDKW3DNV+duaBDeArxZViD7EqPpQxIOawBvl5bs6Radz7OEGZ7khTr2fYU4JGn4WJl61yJg4Xm6pp9cJmi+BIgwLrvXNQKBgQDTPjZ6jCPXAfY6WRVD+hTKgrdhMzNALuiZeyWNSKiiDKgtx1A8OhUAgGzY/PeqmDabiVWKtso+EazfMwa+BrScf+HEZFUvNJ5tGxe6nEEFCx0n5ELUM/L4SWCtD6eFCNYcAY1XvzPD1F3KzewEvw8FbT34fef+YayuIF3PPODtJwKBgQDMgcEnXARRzsIC7i1ggqSU8N0OUSu+rA/hMd9Uh9HsY18p8JtNezAQ2vV4RL3R/CUPGXeDvCBYWhXlkNmjdCAsk24DZHs2Q18xTeHZ15PUtmd2tH/tAxANDi6RTTjpQI3w2poXHl2ZVuT8M+XkTv0WzI0c8TNog2RASzHd5z5JbQKBgQCDlvim5E+bKzywYjfuDYYQFNeZNCTT8aSxn1XoKf/qWooVYlin++KDWnzzurmpSoKR5z4jV/SqL6aJr6aej1zJNJx2E65A5r1d6AejFp0mQCMca4P53paXdlZD2EGZjMSb05extojPj6YRpK9G0aHQ1plJB12SSFQicEUfyKOw9wKBgD09ScLoih6ZRH2uJwZ0eKZlLj0AT5IsYiD0V0Uv2svnwfKEK21bSzxw5Prb0t/TmqFX5fMb3a+3YkE5TALnXk8a4uG/MCpCqHnSMaSTKqCS8o6YZIpr1V2jdoxqTHWEsDyEqYnsvOiTHcTsIZZplN5D6KnXDKbqWZXrLoadnYhNAoGACESLgCy552WcM7vNt4Fw7lR/O3gEnwD41gIx5EGa/UoA08Q+i7sBt9PkL4oQrJ/MYCcVNnmg9KrJdlqF4AlEHYeZSkMuQDYHcaO9xtYP3QdhD+nLXbNrCxaSSaSX8tS4BjdcSH1yMyLFg5OqiJYgwYFiptyKFm5QqFhFTY+20aE=\n-----END PRIVATE KEY----- \ No newline at end of file diff --git a/uaa/src/main/webapp/WEB-INF/spring/oauth-clients.xml b/uaa/src/main/webapp/WEB-INF/spring/oauth-clients.xml index b94fc503bce..cce0782263c 100644 --- a/uaa/src/main/webapp/WEB-INF/spring/oauth-clients.xml +++ b/uaa/src/main/webapp/WEB-INF/spring/oauth-clients.xml @@ -229,6 +229,29 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/PrivateKeyJwtClientAuthIT.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/PrivateKeyJwtClientAuthIT.java new file mode 100644 index 00000000000..3b6088ce9bc --- /dev/null +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/PrivateKeyJwtClientAuthIT.java @@ -0,0 +1,362 @@ +package org.cloudfoundry.identity.uaa.integration.feature; + +import com.fasterxml.jackson.core.type.TypeReference; +import org.cloudfoundry.identity.uaa.ServerRunning; +import org.cloudfoundry.identity.uaa.constants.OriginKeys; +import org.cloudfoundry.identity.uaa.integration.util.IntegrationTestUtils; +import org.cloudfoundry.identity.uaa.oauth.jwt.JwtClientAuthentication; +import org.cloudfoundry.identity.uaa.oauth.jwt.JwtHelper; +import org.cloudfoundry.identity.uaa.provider.IdentityProvider; +import org.cloudfoundry.identity.uaa.provider.OIDCIdentityProviderDefinition; +import org.cloudfoundry.identity.uaa.util.JsonUtils; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.openqa.selenium.WebDriver; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.jwt.Jwt; +import org.springframework.security.oauth2.client.test.TestAccounts; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestOperations; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import static org.cloudfoundry.identity.uaa.provider.ExternalIdentityProviderDefinition.USER_NAME_ATTRIBUTE_NAME; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.springframework.http.HttpStatus.OK; +import static org.springframework.http.HttpStatus.UNAUTHORIZED; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = DefaultIntegrationTestConfig.class) +public class PrivateKeyJwtClientAuthIT { + + //jwt.token.signing-key from uaa.yml + @Value("${integration.test.signing-key}") + private String jwtTokenSigningKey; + + @Autowired + @Rule + public IntegrationTestRule integrationTestRule; + + @Autowired + WebDriver webDriver; + + @Value("${integration.test.base_url}") + String baseUrl; + + @Autowired + TestAccounts testAccounts; + + @Autowired + TestClient testClient; + + @Autowired + RestOperations restOperations; + + @Test + public void testPasswordGrantWithClientUsingPrivateKeyJwtAndExpectValidToken() { + // Given, client with jwks trusted keys + String usedClientId = "client_with_jwks_trust"; + Map expectedClaims = Map.of( + "client_auth_method", "private_key_jwt", + "client_id", usedClientId, + "origin", "uaa", + "zid", "uaa", + "user_name", testAccounts.getUserName(), + "grant_type", "password" + ); + // When + String accessToken = getPasswordGrantToken(usedClientId, "access_token", OK); + // Then + assertNotNull(accessToken); + assertThat(getTokenClaims(accessToken)).containsAllEntriesOf(expectedClaims); + } + + @Test + public void testClientCredentialGrantWithClientUsingPrivateKeyJwtAndExpectValidToken() { + // Given, client with jwks trusted keys + String usedClientId = "client_with_jwks_trust"; + Map expectedClaims = Map.of( + "client_auth_method", "private_key_jwt", + "client_id", usedClientId, + "zid", "uaa", + "grant_type", "client_credentials" + ); + // When + String accessToken = getClientCredentialsGrantToken(usedClientId, "access_token", OK); + // Then + assertNotNull(accessToken); + assertThat(getTokenClaims(accessToken)).containsAllEntriesOf(expectedClaims); + assertThat(getTokenClaims(accessToken)).doesNotContainKeys("user_name", "origin"); + } + + @Test + public void testClientCredentialGrantWithClientUsingPrivateKeyJwtUriAndExpectValidToken() { + // Given, client with jwks_uri keys + String usedClientId = "client_with_allowpublic_and_jwks_uri_trust"; + Map expectedClaims = Map.of( + "client_auth_method", "private_key_jwt", + "client_id", usedClientId, + "zid", "uaa", + "grant_type", "client_credentials" + ); + // When + String accessToken = getClientCredentialsGrantToken(usedClientId, "access_token", OK); + // Then + assertNotNull(accessToken); + assertThat(getTokenClaims(accessToken)).containsAllEntriesOf(expectedClaims); + assertThat(getTokenClaims(accessToken)).doesNotContainKeys("user_name", "origin"); + } + + @Test + public void testRefreshAfterPasswordWithClientUsingPrivateKeyJwtUriAndExpectValidToken() { + // Given, client with jwks_uri keys + String usedClientId = "client_with_allowpublic_and_jwks_uri_trust"; + Map expectedClaims = Map.of( + "client_auth_method", "private_key_jwt", + "client_id", usedClientId, + "origin", "uaa", + "zid", "uaa", + "user_name", testAccounts.getUserName(), + "grant_type", "password" + ); + // When + String refreshToken = getPasswordGrantToken(usedClientId, "refresh_token", OK); + // Then + assertNotNull(refreshToken); + // When + String accessToken = getRefreshGrantToken(usedClientId, refreshToken, "access_token", OK); + // Then + assertNotNull(accessToken); + assertThat(getTokenClaims(accessToken)).containsAllEntriesOf(expectedClaims); + } + + @Test + public void testJwtBearerAfterPasswordWithClientUsingPrivateKeyJwtUriAndExpectValidToken() { + // Given, client with jwks_uri keys + String usedClientId = "client_with_allowpublic_and_jwks_uri_trust"; + Map expectedClaims = Map.of( + "client_auth_method", "private_key_jwt", + "client_id", usedClientId, + "origin", "uaa", + "zid", "uaa", + "user_name", testAccounts.getUserName(), + "grant_type", "password" + ); + // When + String passwordToken = getPasswordGrantToken(usedClientId, "access_token", OK); + // Then + assertNotNull(passwordToken); + assertThat(getTokenClaims(passwordToken)).containsAllEntriesOf(expectedClaims); + // When + String accessToken = getJwtBearerGrantToken(usedClientId, passwordToken, "access_token", OK); + // Then + assertNotNull(accessToken); + assertThat(getTokenClaims(accessToken)).containsAllEntriesOf(Map.of( + "client_auth_method", "private_key_jwt", + "client_id", usedClientId, + "origin", "uaa", + "zid", "uaa", + "user_name", testAccounts.getUserName(), + "grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer" + )); + } + + @Test + public void testPasswordGrantWithClientUsingOidcProxyAndExpectValidToken() throws Exception { + // Given + String usedClientId = "client_with_allowpublic_and_jwks_uri_trust"; + String expectedOriginKey = "oidc-proxy"; + Map expectedClaims = Map.of( + "client_auth_method", "private_key_jwt", + "client_id", usedClientId, + "origin", expectedOriginKey, + "zid", "uaa", + "user_name", testAccounts.getUserName(), + "grant_type", "password" + ); + String clientCredentialsToken = IntegrationTestUtils.getClientCredentialsToken(baseUrl, "admin", "adminsecret"); + try { + // create OIDC IdP using private_key_jwt with jwks trust + IdentityProvider oidcProxy = createOidcProviderTemplate("client_with_jwks_trust", expectedOriginKey); + IntegrationTestUtils.createOrUpdateProvider(clientCredentialsToken, baseUrl, oidcProxy); + // When Password Grant with OIDC proxy in between + String accessToken = getPasswordProxyGrantToken(usedClientId, "access_token", expectedOriginKey, OK); + // Then + assertNotNull(accessToken); + assertThat(getTokenClaims(accessToken)).containsAllEntriesOf(expectedClaims); + } finally { + IntegrationTestUtils.deleteProvider(clientCredentialsToken, baseUrl, "uaa", expectedOriginKey); + } + } + + @Test + public void testAutorizationCodeGrantWithClientUsingOidcProxyAndExpectValidToken() throws Exception { + // Given + String expectedOriginKey = "oidc-proxy-private-key-jwt"; + String usedClientId = "client_with_allowpublic_and_jwks_uri_trust"; + String clientCredentialsToken = IntegrationTestUtils.getClientCredentialsToken(baseUrl, "admin", "adminsecret"); + try { + // create OIDC IdP using private_key_jwt with jwks trust + IdentityProvider oidcProxy = createOidcProviderTemplate("client_with_jwks_trust", expectedOriginKey); + IntegrationTestUtils.createOrUpdateProvider(clientCredentialsToken, baseUrl, oidcProxy); + ServerRunning serverRunning = ServerRunning.isRunning(); + serverRunning.setHostName("localhost"); + // login + String accessToken = IntegrationTestUtils.getAuthorizationCodeToken( + serverRunning, + usedClientId, + testClient.createClientJwt(usedClientId, jwtTokenSigningKey), + testAccounts.getUserName(), + testAccounts.getPassword(), + null, + baseUrl + "/login/callback/" + expectedOriginKey, + expectedOriginKey, + false); + assertThat(getTokenClaims(accessToken)).containsAllEntriesOf(Map.of( + "client_auth_method", "private_key_jwt", + "client_id", usedClientId, + "origin", "uaa", + "zid", "uaa", + "user_name", testAccounts.getUserName(), + "grant_type", "authorization_code" + )); + } finally { + IntegrationTestUtils.deleteProvider(clientCredentialsToken, baseUrl, "uaa", expectedOriginKey); + } + } + + @Test + public void testPasswordGrantWithClientUsingPrivateKeyJwtAndExpectClientError() { + // When + String response = getPasswordGrantToken("admin", "access_token", UNAUTHORIZED); + // Then + assertNotNull(response); + assertThat(response).contains("401"); + } + + @Test + public void testClientCredentialGrantWithClientUsingPrivateKeyJwtAndExpectClientError() { + // When + String response = getClientCredentialsGrantToken("any-other-not-existing-client", "access_token", UNAUTHORIZED); + // Then + assertNotNull(response); + assertThat(response).contains("401"); + } + + private String getClientCredentialsGrantToken(String clientId, String returnToken, HttpStatus expected) { + return getToken(clientId, returnToken, expected, "client_credentials", null, null, null, null); + } + + private String getPasswordGrantToken(String clientId, String returnToken, HttpStatus expected) { + return getToken(clientId, returnToken, expected, "password", testAccounts.getUserName(), testAccounts.getPassword(), null, null); + } + + private String getPasswordProxyGrantToken(String clientId, String returnToken, String origin, HttpStatus expected) { + return getToken(clientId, returnToken, expected, "password", testAccounts.getUserName(), testAccounts.getPassword(), "login_hint", "{\"origin\":\""+origin+"\"}"); + } + + private String getRefreshGrantToken(String clientId, String refreshTokenValue, String returnToken, HttpStatus expected) { + return getToken(clientId, returnToken, expected, "refresh_token", null, null, "refresh_token", refreshTokenValue); + } + + private String getJwtBearerGrantToken(String clientId, String bearerToken, String returnToken, HttpStatus expected) { + return getToken(clientId, returnToken, expected, "urn:ietf:params:oauth:grant-type:jwt-bearer", null, null, "assertion", bearerToken); + } + + private String getToken(String clientId, String returnToken, HttpStatus expected, String grantType, String userName, String password, String extraKey, String extraValue) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + + LinkedMultiValueMap postBody = new LinkedMultiValueMap<>(); + postBody.add("grant_type", grantType); + if (userName != null) { + postBody.add("username", userName); + } + if (password != null) { + postBody.add("password", password); + } + if (extraKey != null && extraValue != null) { + postBody.add(extraKey, extraValue); + } + postBody.add("token_format", "jwt"); + postBody.add(JwtClientAuthentication.CLIENT_ASSERTION, testClient.createClientJwt(clientId, jwtTokenSigningKey)); + postBody.add(JwtClientAuthentication.CLIENT_ASSERTION_TYPE, JwtClientAuthentication.GRANT_TYPE); + + ResponseEntity responseEntity; + try { + responseEntity = restOperations.exchange(baseUrl + "/oauth/token", HttpMethod.POST, new HttpEntity<>(postBody, headers), Map.class); + } catch (HttpClientErrorException e) { + assertNotEquals("Expected OK, but the call failed", expected, OK); + return e.getMessage(); + } + assertEquals(expected, responseEntity.getStatusCode()); + if (expected == OK) { + return (String) responseEntity.getBody().get(returnToken); + } else { + fail("not expected"); + return null; + } + } + + private static Map getTokenClaims(String token) { + Jwt tokenClaims = JwtHelper.decode(token); + return JsonUtils.readValue(tokenClaims.getClaims(), new TypeReference>() {}); + } + + private IdentityProvider createOidcProviderTemplate(String clientId, String origin) throws MalformedURLException { + IdentityProvider identityProvider = new IdentityProvider<>(); + identityProvider.setName("my oidc provider"); + identityProvider.setIdentityZoneId(OriginKeys.UAA); + OIDCIdentityProviderDefinition config = new OIDCIdentityProviderDefinition(); + config.setClientAuthInBody(false); + config.addAttributeMapping(USER_NAME_ATTRIBUTE_NAME, "user_name"); + config.addAttributeMapping("given_name", "email"); + config.addAttributeMapping("family_name", "email"); + config.addAttributeMapping("external_groups", "scope"); + config.setStoreCustomAttributes(true); + config.addWhiteListedGroup("*"); + config.setAuthUrl(new URL(baseUrl + "/oauth/authorize")); + config.setTokenUrl(new URL(baseUrl + "/oauth/token")); + config.setTokenKeyUrl(new URL(baseUrl + "/token_key")); + config.setIssuer(baseUrl + "/oauth/token"); + config.setUserInfoUrl(new URL(baseUrl + "/userinfo")); + + config.setShowLinkText(true); + config.setLinkText("My OIDC Proxy Provider"); + config.setSkipSslValidation(true); + config.setRelyingPartyId(clientId); + config.setRelyingPartySecret(null); + config.setJwtClientAuthentication(Boolean.TRUE); + config.setPasswordGrantEnabled(true); + List requestedScopes = new ArrayList<>(); + requestedScopes.add("openid"); + requestedScopes.add("cloud_controller.read"); + config.setScopes(requestedScopes); + identityProvider.setConfig(config); + identityProvider.setOriginKey(origin); + identityProvider.setIdentityZoneId("uaa"); + return identityProvider; + } + +} diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/TestClient.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/TestClient.java index dbea3a4493f..f559bdad509 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/TestClient.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/TestClient.java @@ -13,6 +13,10 @@ package org.cloudfoundry.identity.uaa.integration.feature; import org.apache.commons.codec.binary.Base64; +import org.cloudfoundry.identity.uaa.impl.config.LegacyTokenKey; +import org.cloudfoundry.identity.uaa.oauth.KeyInfoService; +import org.cloudfoundry.identity.uaa.oauth.jwt.JwtClientAuthentication; +import org.cloudfoundry.identity.uaa.provider.OIDCIdentityProviderDefinition; import org.cloudfoundry.identity.uaa.util.JsonUtils; import org.junit.Assert; import org.springframework.http.HttpEntity; @@ -26,6 +30,8 @@ import org.springframework.web.client.RestTemplate; import org.springframework.web.util.HtmlUtils; +import java.net.MalformedURLException; +import java.net.URL; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -88,6 +94,20 @@ public void createScimClient(String adminAccessToken, String clientId) { ); } + public String createClientJwt(String clientId, String jwks) { + KeyInfoService keyInfoService = new KeyInfoService(baseUrl); + JwtClientAuthentication jwtClientAuthentication = new JwtClientAuthentication(keyInfoService); + OIDCIdentityProviderDefinition config = new OIDCIdentityProviderDefinition(); + config.setRelyingPartyId(clientId); + try { + config.setTokenUrl(new URL(baseUrl + "/oauth/token")); + } catch (MalformedURLException e) { + return ""; + } + LegacyTokenKey.setLegacySigningKey(jwks, baseUrl); + return jwtClientAuthentication.getClientAssertion(config); + } + public void createUser(String scimAccessToken, String userName, String email, String password, Boolean verified) { restfulCreate( diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/util/IntegrationTestUtils.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/util/IntegrationTestUtils.java index 6af2d117b16..7f7504ed6a3 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/util/IntegrationTestUtils.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/util/IntegrationTestUtils.java @@ -16,6 +16,7 @@ import org.cloudfoundry.identity.uaa.integration.feature.TestClient; import org.cloudfoundry.identity.uaa.mfa.GoogleMfaProviderConfig; import org.cloudfoundry.identity.uaa.mfa.MfaProvider; +import org.cloudfoundry.identity.uaa.oauth.jwt.JwtClientAuthentication; import org.cloudfoundry.identity.uaa.provider.AbstractExternalOAuthIdentityProviderDefinition; import org.cloudfoundry.identity.uaa.provider.IdentityProvider; import org.cloudfoundry.identity.uaa.provider.OIDCIdentityProviderDefinition; @@ -1289,10 +1290,43 @@ public static void callCheckToken(ServerRunning serverRunning, assertNotNull(tokenResponse.getBody().get("iss")); } + public static String getAuthorizationCodeToken( + ServerRunning serverRunning, + String clientId, + String clientAssertion, + String username, + String password, + String tokenResponseType, + String redirectUri, + String loginHint, + boolean callCheckToken) + { + return getAuthorizationCodeTokenMap(serverRunning, UaaTestAccounts.standard(serverRunning), clientId, null, clientAssertion, + username, password, tokenResponseType, null, redirectUri, loginHint, callCheckToken).get("access_token"); + } + + public static Map getAuthorizationCodeTokenMap( + ServerRunning serverRunning, + UaaTestAccounts testAccounts, + String clientId, + String clientSecret, + String username, + String password, + String tokenResponseType, + String jSessionId, + String redirectUri, + String loginHint, + boolean callCheckToken) + { + return getAuthorizationCodeTokenMap(serverRunning, testAccounts, clientId, clientSecret, null, username, password, + tokenResponseType, jSessionId, redirectUri, loginHint, callCheckToken); + } + public static Map getAuthorizationCodeTokenMap(ServerRunning serverRunning, UaaTestAccounts testAccounts, String clientId, String clientSecret, + String clientAssertion, String username, String password, String tokenResponseType, @@ -1410,7 +1444,12 @@ public static Map getAuthorizationCodeTokenMap(ServerRunning ser } formData.add("code", location.split("code=")[1].split("&")[0]); HttpHeaders tokenHeaders = new HttpHeaders(); - tokenHeaders.set("Authorization", testAccounts.getAuthorizationHeader(clientId, clientSecret)); + if (clientSecret != null) { + tokenHeaders.set("Authorization", testAccounts.getAuthorizationHeader(clientId, clientSecret)); + } else if (clientAssertion != null) { + formData.add(JwtClientAuthentication.CLIENT_ASSERTION_TYPE, JwtClientAuthentication.GRANT_TYPE); + formData.add(JwtClientAuthentication.CLIENT_ASSERTION, clientAssertion); + } @SuppressWarnings("rawtypes") ResponseEntity tokenResponse = serverRunning.postForMap("/oauth/token", formData, tokenHeaders); assertEquals(HttpStatus.OK, tokenResponse.getStatusCode()); @@ -1421,7 +1460,12 @@ public static Map getAuthorizationCodeTokenMap(ServerRunning ser formData = new LinkedMultiValueMap<>(); HttpHeaders headers = new HttpHeaders(); - headers.set("Authorization", testAccounts.getAuthorizationHeader(clientId, clientSecret)); + if (clientSecret != null) { + headers.set("Authorization", testAccounts.getAuthorizationHeader(clientId, clientSecret)); + } else if (clientAssertion != null) { + formData.add(JwtClientAuthentication.CLIENT_ASSERTION_TYPE, JwtClientAuthentication.GRANT_TYPE); + formData.add(JwtClientAuthentication.CLIENT_ASSERTION, clientAssertion); + } formData.add("token", accessToken.getValue()); if (callCheckToken) {