diff --git a/.github/workflows/dependency-updater-7.yml b/.github/workflows/dependency-updater-7.yml
index d9c4a2164ae..b31f43f79ea 100644
--- a/.github/workflows/dependency-updater-7.yml
+++ b/.github/workflows/dependency-updater-7.yml
@@ -1,5 +1,5 @@
# This workflow will update dependencies for product-is 7.x.x.
-name: Dependency Updater 7.x.x
+name: "[Deprecated] Dependency Updater 7.x.x"
on:
workflow_dispatch:
# schedule:
diff --git a/.github/workflows/pr-builder-test-JDK11-7.yml b/.github/workflows/pr-builder-test-JDK11-7.yml
index ba39fa4afc9..63fa41bc3dc 100644
--- a/.github/workflows/pr-builder-test-JDK11-7.yml
+++ b/.github/workflows/pr-builder-test-JDK11-7.yml
@@ -1,4 +1,4 @@
-name: pr-builder-test 7.x.x
+name: "[Deprecated] pr-builder-test 7.x.x"
on:
workflow_dispatch:
diff --git a/modules/distribution/src/repository/resources/conf/templates/repository/conf/tomcat/catalina-server.xml.j2 b/modules/distribution/src/repository/resources/conf/templates/repository/conf/tomcat/catalina-server.xml.j2
index 499861243b9..e32d0405d2a 100644
--- a/modules/distribution/src/repository/resources/conf/templates/repository/conf/tomcat/catalina-server.xml.j2
+++ b/modules/distribution/src/repository/resources/conf/templates/repository/conf/tomcat/catalina-server.xml.j2
@@ -109,6 +109,11 @@
+
+ {% if tenant_context.enable_tenant_qualified_urls is sameas false %}
+
+ {% endif %}
+
diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java
index 67352c5ca4e..f956ba51848 100644
--- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java
+++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java
@@ -489,6 +489,31 @@ public HttpResponse sendLoginPostForCustomUsers(HttpClient client, String sessio
return sendPostRequestWithParameters(client, urlParameters, OAuth2Constant.COMMON_AUTH_URL);
}
+ /**
+ * Send login post request for a tenant with given username and password credentials.
+ *
+ * @param client Http client.
+ * @param sessionDataKey Session data key.
+ * @param username Username.
+ * @param password Password.
+ * @param tenantDomain Tenant domain.
+ * @return Http response.
+ * @throws ClientProtocolException ClientProtocolException
+ * @throws IOException IOException
+ */
+ public HttpResponse sendLoginPostForCustomUsers(HttpClient client, String sessionDataKey, String username,
+ String password, String tenantDomain)
+ throws ClientProtocolException, IOException {
+
+ List urlParameters = new ArrayList<>();
+ urlParameters.add(new BasicNameValuePair("username", username));
+ urlParameters.add(new BasicNameValuePair("password", password));
+ urlParameters.add(new BasicNameValuePair("sessionDataKey", sessionDataKey));
+ log.info(">>> sendLoginPost:sessionDataKey: " + sessionDataKey);
+ String url = OAuth2Constant.TENANT_COMMON_AUTH_URL.replace(OAuth2Constant.TENANT_PLACEHOLDER, tenantDomain);
+ return sendPostRequestWithParameters(client, urlParameters, url);
+ }
+
/**
* Send approval post request
*
@@ -531,6 +556,33 @@ public HttpResponse sendApprovalPostWithConsent(HttpClient client, String sessio
return sendPostRequestWithParameters(client, urlParameters, OAuth2Constant.APPROVAL_URL);
}
+ /**
+ * Send approval post request for tenant with consent.
+ *
+ * @param client http client.
+ * @param sessionDataKeyConsent session consent data.
+ * @param consentClaims claims requiring user consent.
+ * @param tenantDomain tenant domain.
+ * @return http response.
+ * @throws java.io.IOException IOException.
+ */
+ public HttpResponse sendApprovalPostWithConsent(HttpClient client, String sessionDataKeyConsent,
+ List consentClaims, String tenantDomain)
+ throws IOException {
+
+ List urlParameters = new ArrayList<>();
+ urlParameters.add(new BasicNameValuePair("consent", "approve"));
+ urlParameters.add(new BasicNameValuePair("scope-approval", "approve"));
+ urlParameters.add(new BasicNameValuePair("sessionDataKeyConsent", sessionDataKeyConsent));
+
+ if (consentClaims != null) {
+ urlParameters.addAll(consentClaims);
+ }
+ String url = OAuth2Constant.TENANT_APPROVAL_URL.replace(OAuth2Constant.TENANT_PLACEHOLDER, tenantDomain);
+
+ return sendPostRequestWithParameters(client, urlParameters, url);
+ }
+
/**
* Send approval post request
*
diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuthAppsWithSameClientIdTestCase.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuthAppsWithSameClientIdTestCase.java
new file mode 100644
index 00000000000..26a3dfe7720
--- /dev/null
+++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuthAppsWithSameClientIdTestCase.java
@@ -0,0 +1,738 @@
+/*
+ * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.identity.integration.test.oauth2;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.NameValuePair;
+import org.apache.http.StatusLine;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.CookieSpecs;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.config.Lookup;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.cookie.CookieSpecProvider;
+import org.apache.http.impl.client.BasicCookieStore;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.cookie.RFC6265CookieSpecProvider;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+import org.json.JSONException;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.wso2.carbon.automation.engine.context.beans.Tenant;
+import org.wso2.carbon.automation.engine.context.beans.User;
+import org.wso2.carbon.integration.common.utils.exceptions.AutomationUtilException;
+import org.wso2.carbon.integration.common.utils.mgt.ServerConfigurationManager;
+import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.ApplicationListItem;
+import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.ApplicationListResponse;
+import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.ApplicationModel;
+import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.ApplicationResponseModel;
+import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.InboundProtocols;
+import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.OpenIDConnectConfiguration;
+import org.wso2.identity.integration.test.rest.api.server.tenant.management.v1.model.Owner;
+import org.wso2.identity.integration.test.rest.api.server.tenant.management.v1.model.TenantModel;
+import org.wso2.identity.integration.test.rest.api.user.common.model.Email;
+import org.wso2.identity.integration.test.rest.api.user.common.model.UserObject;
+import org.wso2.identity.integration.test.restclients.OAuth2RestClient;
+import org.wso2.identity.integration.test.restclients.RestBaseClient;
+import org.wso2.identity.integration.test.restclients.SCIM2RestClient;
+import org.wso2.identity.integration.test.restclients.TenantMgtRestClient;
+import org.wso2.identity.integration.test.util.Utils;
+import org.wso2.identity.integration.test.utils.CommonConstants;
+import org.wso2.identity.integration.test.utils.DataExtractUtil;
+import org.wso2.identity.integration.test.utils.OAuth2Constant;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class contains tests for OAuth apps with same client id in multiple tenants.
+ */
+public class OAuthAppsWithSameClientIdTestCase extends OAuth2ServiceAbstractIntegrationTest {
+
+ public final static String DUMMY_CLIENT_ID = "dummy_client_id";
+ public final static String DUMMY_CLIENT_SECRET = "dummy_client_secret";
+ private static final String TENANT_1_DOMAIN = "tenant1.com";
+ private static final String TENANT_1_ADMIN_USERNAME = "admin@tenant1.com";
+ private static final String TENANT_1_ADMIN_PASSWORD = "admin";
+ private static final String TENANT_1_ADMIN_TENANT_AWARE_USERNAME = "admin";
+ private static final String TENANT_1_USER_USERNAME = "userTenant1";
+ private static final String TENANT_1_USER_PASSWORD = "userTenant1";
+ private static final String TENANT_1_USER_EMAIL = "userTenant1@wso2.com";
+ private static final String TENANT_2_DOMAIN = "tenant2.com";
+ private static final String TENANT_2_ADMIN_USERNAME = "admin@tenant2.com";
+ private static final String TENANT_2_ADMIN_PASSWORD = "admin";
+ private static final String TENANT_2_ADMIN_TENANT_AWARE_USERNAME = "admin";
+ private static final String TENANT_2_USER_USERNAME = "userTenant2";
+ private static final String TENANT_2_USER_PASSWORD = "userTenant2";
+ private static final String TENANT_2_USER_EMAIL = "userTenant2@wso2.com";
+ public final static String TENANT_1_AUTHORIZE_URL = "https://localhost:9853/t/tenant1.com/oauth2/authorize";
+
+ private ServerConfigurationManager serverConfigurationManager;
+ private TenantMgtRestClient tenantMgtRestClient;
+ private OAuth2RestClient oAuth2RestClientTenant1;
+ private OAuth2RestClient oAuth2RestClientTenant2;
+ private SCIM2RestClient scim2RestClientTenant1;
+ private SCIM2RestClient scim2RestClientTenant2;
+ private HttpClient client;
+
+ private boolean configurationsRestored = false;
+ private String tenant1UserId = null;
+ private String tenant2UserId = null;
+ private String sessionDataKey;
+ private String sessionDataKeyConsent;
+ private String authorizationCode;
+ private String accessToken;
+
+ @BeforeClass(alwaysRun = true)
+ private void testInit() throws Exception {
+
+ super.init();
+ serverConfigurationManager = new ServerConfigurationManager(isServer);
+
+ // Enable tenant qualified urls/ tenanted sessions and restart the server.
+ changeISConfigurations();
+
+ tenantMgtRestClient = new TenantMgtRestClient(serverURL, tenantInfo);
+
+ // Create the test tenants.
+ addTenant(TENANT_1_DOMAIN, TENANT_1_ADMIN_USERNAME, TENANT_1_ADMIN_PASSWORD,
+ TENANT_1_ADMIN_TENANT_AWARE_USERNAME);
+ addTenant(TENANT_2_DOMAIN, TENANT_2_ADMIN_USERNAME, TENANT_2_ADMIN_PASSWORD,
+ TENANT_2_ADMIN_TENANT_AWARE_USERNAME);
+
+ // Create rest clients.
+ Tenant tenant1 = getTenantInfo(TENANT_1_DOMAIN);
+ oAuth2RestClientTenant1 = new OAuth2RestClient(serverURL, tenant1);
+ scim2RestClientTenant1 = new SCIM2RestClient(serverURL, tenant1);
+ Tenant tenant2 = getTenantInfo(TENANT_2_DOMAIN);
+ oAuth2RestClientTenant2 = new OAuth2RestClient(serverURL, tenant2);
+ scim2RestClientTenant2 = new SCIM2RestClient(serverURL, tenant2);
+
+ // Create http client.
+ RequestConfig requestConfig = RequestConfig.custom()
+ .setCookieSpec(CookieSpecs.DEFAULT)
+ .build();
+ Lookup cookieSpecRegistry = RegistryBuilder.create()
+ .register(CookieSpecs.DEFAULT, new RFC6265CookieSpecProvider())
+ .build();
+
+ client = HttpClientBuilder.create()
+ .setDefaultCookieStore(new BasicCookieStore())
+ .setDefaultRequestConfig(requestConfig)
+ .setDefaultCookieSpecRegistry(cookieSpecRegistry)
+ .build();
+ }
+
+ @AfterClass(alwaysRun = true)
+ public void testClear() throws IOException, AutomationUtilException {
+
+ restoreISConfigurations();
+ tenantMgtRestClient.closeHttpClient();
+ oAuth2RestClientTenant1.closeHttpClient();
+ oAuth2RestClientTenant2.closeHttpClient();
+ }
+
+ @AfterMethod
+ public void afterMethod() throws IOException {
+
+ deleteAllApplications(TENANT_1_DOMAIN);
+ deleteAllApplications(TENANT_2_DOMAIN);
+
+ if (StringUtils.isNotEmpty(tenant1UserId)) {
+ getSCIMRestClient(TENANT_1_DOMAIN).deleteUser(tenant1UserId);
+ tenant1UserId = null;
+ }
+ if (StringUtils.isNotEmpty(tenant2UserId)) {
+ getSCIMRestClient(TENANT_2_DOMAIN).deleteUser(tenant2UserId);
+ tenant2UserId = null;
+ }
+
+ sessionDataKey = null;
+ sessionDataKeyConsent = null;
+ authorizationCode = null;
+ accessToken = null;
+ }
+
+ @Test(description = "Create two OAuth apps in the same tenant with same client id.")
+ public void testCreateAppsWithSameClientIdInSameTenant() throws Exception {
+
+ // Create first app.
+ ApplicationResponseModel app1 = createApplication(TENANT_1_DOMAIN);
+ Assert.assertNotNull(app1, "OAuth app creation failed for tenant 1.");
+
+ // Try to create second app with the same client id and expect a conflict.
+ StatusLine statusLine = createApplicationWithResponse(TENANT_1_DOMAIN);
+ Assert.assertEquals(statusLine.getStatusCode(), HttpStatus.SC_CONFLICT, "Expected status code not received.");
+ }
+
+ @Test(description = "Create two OAuth apps in two tenants with the same client id.",
+ dependsOnMethods = "testCreateAppsWithSameClientIdInSameTenant")
+ public void testCreateAppsWithSameClientIdInMultipleTenants() throws Exception {
+
+ // Create oauth apps in both tenants.
+ ApplicationResponseModel tenant1App = createApplication(TENANT_1_DOMAIN);
+ ApplicationResponseModel tenant2App = createApplication(TENANT_2_DOMAIN);
+
+ // Assertions for inbound configurations.
+ Assert.assertNotNull(tenant1App, "OAuth app creation failed for tenant 1.");
+ OpenIDConnectConfiguration tenant1AppConfig = getOAuthRestClient(TENANT_1_DOMAIN)
+ .getOIDCInboundDetails(tenant1App.getId());
+ Assert.assertNotNull(tenant1AppConfig.getClientId());
+ Assert.assertNotNull(tenant1AppConfig.getClientSecret());
+
+ Assert.assertNotNull(tenant2App, "OAuth app creation failed for tenant 2.");
+ OpenIDConnectConfiguration tenant2AppConfig = getOAuthRestClient(TENANT_2_DOMAIN)
+ .getOIDCInboundDetails(tenant2App.getId());
+ Assert.assertNotNull(tenant2AppConfig.getClientId());
+ Assert.assertNotNull(tenant2AppConfig.getClientSecret());
+ }
+
+ @Test(description = "Create two OAuth apps in two tenants with the same client id and retrieve " +
+ "tenant app by client id.", dependsOnMethods = "testCreateAppsWithSameClientIdInMultipleTenants")
+ public void testRetrieveTenantAppByClientId() throws JSONException, IOException {
+
+ // Create oauth apps in both tenants.
+ ApplicationResponseModel tenant1App = createApplication(TENANT_1_DOMAIN);
+ ApplicationResponseModel tenant2App = createApplication(TENANT_2_DOMAIN);
+
+ // Assertions for successful app creation.
+ Assert.assertNotNull(tenant1App, "OAuth app creation failed for tenant 1.");
+ Assert.assertNotNull(tenant2App, "OAuth app creation failed for tenant 2.");
+
+ // Retrieve app by client id.
+ List applications = getOAuthRestClient(TENANT_1_DOMAIN)
+ .getApplicationsByClientId(DUMMY_CLIENT_ID);
+ Assert.assertEquals(applications.size(), 1);
+ Assert.assertEquals(applications.get(0).getId(), tenant1App.getId());
+ }
+
+ @Test(description = "Create two OAuth apps in two tenants with the same client id and update " +
+ "inbound configurations of one app.", dependsOnMethods = "testRetrieveTenantAppByClientId")
+ public void testCreateAppsAndUpdateInboundConfigurationsOfOne() throws Exception {
+
+ // Create oauth apps in both tenants.
+ ApplicationResponseModel tenant1App = createApplication(TENANT_1_DOMAIN);
+ ApplicationResponseModel tenant2App = createApplication(TENANT_2_DOMAIN);
+
+ // Assert for created app.
+ Assert.assertNotNull(tenant1App, "OAuth app creation failed for tenant 1.");
+ Assert.assertNotNull(tenant2App, "OAuth app creation failed for tenant 2.");
+
+ // Update inbound configs of tenant 1 app.
+ OpenIDConnectConfiguration tenant1AppConfig = getOAuthRestClient(TENANT_1_DOMAIN)
+ .getOIDCInboundDetails(tenant1App.getId());
+ List grantTypes = new ArrayList<>();
+ Collections.addAll(grantTypes, OAuth2Constant.OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE);
+ tenant1AppConfig.setGrantTypes(grantTypes);
+ List callBackUrls = new ArrayList<>();
+ Collections.addAll(callBackUrls, "http://localhost:8490/updated/callback");
+ tenant1AppConfig.setCallbackURLs(callBackUrls);
+
+ getOAuthRestClient(TENANT_1_DOMAIN).updateInboundDetailsOfApplication(tenant1App.getId(), tenant1AppConfig,
+ RestBaseClient.OIDC);
+
+ // Assert for updated inbound configs.
+ OpenIDConnectConfiguration updatedTenant1AppConfig = getOAuthRestClient(TENANT_1_DOMAIN)
+ .getOIDCInboundDetails(tenant1App.getId());
+ Assert.assertNotNull(updatedTenant1AppConfig.getClientId());
+ Assert.assertNotNull(updatedTenant1AppConfig.getClientSecret());
+ Assert.assertEquals(updatedTenant1AppConfig.getGrantTypes(), grantTypes);
+ Assert.assertEquals(updatedTenant1AppConfig.getCallbackURLs(), callBackUrls);
+
+ // Assert inbound config of tenant 2 app is not updated.
+ OpenIDConnectConfiguration tenant2AppConfig = getOAuthRestClient(TENANT_2_DOMAIN)
+ .getOIDCInboundDetails(tenant2App.getId());
+ Assert.assertNotNull(tenant2AppConfig.getClientId());
+ Assert.assertNotNull(tenant2AppConfig.getClientSecret());
+
+ List originalCallBackUrls = new ArrayList<>();
+ Collections.addAll(originalCallBackUrls, OAuth2Constant.CALLBACK_URL);
+ Assert.assertEquals(tenant2AppConfig.getCallbackURLs(), originalCallBackUrls);
+
+ List originalGrantTypes = new ArrayList<>();
+ Collections.addAll(originalGrantTypes, OAuth2Constant.OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE,
+ OAuth2Constant.OAUTH2_GRANT_TYPE_CLIENT_CREDENTIALS,
+ OAuth2Constant.OAUTH2_GRANT_TYPE_RESOURCE_OWNER,
+ OAuth2Constant.OAUTH2_GRANT_TYPE_REFRESH_TOKEN);
+ Assert.assertEquals(tenant2AppConfig.getGrantTypes(), originalGrantTypes);
+ }
+
+ @Test(description = "Create two OAuth apps in two tenants with the same client id and delete an app " +
+ "in one tenant.", dependsOnMethods = "testCreateAppsAndUpdateInboundConfigurationsOfOne")
+ public void testCreateAppsAndDeleteOne() throws Exception {
+
+ // Create oauth apps in both tenants.
+ ApplicationResponseModel tenant1App = createApplication(TENANT_1_DOMAIN);
+ ApplicationResponseModel tenant2App = createApplication(TENANT_2_DOMAIN);
+
+ // Assertions for successful app creation.
+ Assert.assertNotNull(tenant1App, "OAuth app creation failed for tenant 1.");
+ Assert.assertNotNull(tenant2App, "OAuth app creation failed for tenant 2.");
+
+ // Delete tenant 1 app and assert for tenant 2 app.
+ getOAuthRestClient(TENANT_1_DOMAIN).deleteApplication(tenant1App.getId());
+ ApplicationListResponse tenant1Apps = getOAuthRestClient(TENANT_1_DOMAIN).getAllApplications();
+ Assert.assertEquals(tenant1Apps.getApplications().size(), 0);
+ ApplicationListResponse tenant2Apps = getOAuthRestClient(TENANT_2_DOMAIN).getAllApplications();
+ Assert.assertEquals(tenant2Apps.getApplications().size(), 1);
+ }
+
+ @Test(description = "Create two OAuth apps in two tenants with the same client id and delete " +
+ "inbound configurations of one app.", dependsOnMethods = "testCreateAppsAndDeleteOne")
+ public void testCreateAppsAndDeleteInboundConfigurationsOfOne() throws Exception {
+
+ // Create oauth apps in both tenants.
+ ApplicationResponseModel tenant1App = createApplication(TENANT_1_DOMAIN);
+ ApplicationResponseModel tenant2App = createApplication(TENANT_2_DOMAIN);
+
+ // Assertions for successful app creation.
+ Assert.assertNotNull(tenant1App, "OAuth app creation failed for tenant 1.");
+ Assert.assertNotNull(tenant2App, "OAuth app creation failed for tenant 2.");
+
+ // Delete OIDC inbound configurations of tenant 1 app.
+ getOAuthRestClient(TENANT_1_DOMAIN).deleteInboundConfiguration(tenant1App.getId(), RestBaseClient.OIDC);
+
+ // Try to retrieve inbound configs of tenant 1 app and expect an exception.
+ try {
+ OpenIDConnectConfiguration tenant1AppConfig = getOAuthRestClient(TENANT_1_DOMAIN)
+ .getOIDCInboundDetails(tenant1App.getId());
+ Assert.fail("Expected exception not received.");
+ } catch (Exception e) {
+ Assert.assertNotNull(e.getMessage());
+ }
+
+ // Assert inbound config of tenant 2 app is not deleted.
+ OpenIDConnectConfiguration tenant2AppConfig = getOAuthRestClient(TENANT_2_DOMAIN)
+ .getOIDCInboundDetails(tenant2App.getId());
+ Assert.assertNotNull(tenant2AppConfig.getClientId());
+ Assert.assertNotNull(tenant2AppConfig.getClientSecret());
+ }
+
+ @Test(description = "Create two OAuth apps in two tenants with the same client id and try to login " +
+ "with the user for the tenant.", dependsOnMethods = "testCreateAppsAndDeleteInboundConfigurationsOfOne")
+ public void testOAuthApplicationLoginSuccess() throws Exception {
+
+ // Create oauth apps in both tenants.
+ ApplicationResponseModel tenant1App = createApplication(TENANT_1_DOMAIN);
+ ApplicationResponseModel tenant2App = createApplication(TENANT_2_DOMAIN);
+ Assert.assertNotNull(tenant1App, "OAuth app creation failed for tenant 1.");
+ Assert.assertNotNull(tenant2App, "OAuth app creation failed for tenant 2.");
+
+ // Create users.
+ tenant1UserId = createUser(TENANT_1_DOMAIN);
+ tenant2UserId = createUser(TENANT_2_DOMAIN);
+
+ // Authenticate.
+ initiateAuthorizationRequest(true);
+ authenticateUser(true, TENANT_1_USER_USERNAME, TENANT_1_USER_PASSWORD, TENANT_1_DOMAIN);
+ performConsentApproval(true, TENANT_1_DOMAIN);
+ generateAuthzCodeAccessToken(true, TENANT_1_DOMAIN);
+ introspectActiveAccessToken(TENANT_1_DOMAIN, TENANT_1_ADMIN_USERNAME, TENANT_1_ADMIN_PASSWORD);
+ }
+
+ @Test(description = "Create two OAuth apps in two tenants with the same client id and try to login " +
+ "with the user from the other tenant.", dependsOnMethods = "testOAuthApplicationLoginSuccess",
+ expectedExceptions = AssertionError.class)
+ public void testOAuthApplicationLoginIncorrectTenant() throws Exception {
+
+ // Create oauth apps in both tenants.
+ ApplicationResponseModel tenant1App = createApplication(TENANT_1_DOMAIN);
+ ApplicationResponseModel tenant2App = createApplication(TENANT_2_DOMAIN);
+ Assert.assertNotNull(tenant1App, "OAuth app creation failed for tenant 1.");
+ Assert.assertNotNull(tenant2App, "OAuth app creation failed for tenant 2.");
+
+ // Create users.
+ tenant1UserId = createUser(TENANT_1_DOMAIN);
+ tenant2UserId = createUser(TENANT_2_DOMAIN);
+
+ // Authenticate.
+ initiateAuthorizationRequest(true);
+ authenticateUser(true, TENANT_2_USER_USERNAME, TENANT_2_USER_PASSWORD, TENANT_1_DOMAIN);
+ Assert.fail("Expected exception not received.");
+ }
+
+ @Test(description = "Create an OAuth app in tenant 1 and try to login when tenant qualified urls are disabled.",
+ dependsOnMethods = "testOAuthApplicationLoginIncorrectTenant")
+ public void testOAuthApplicationLoginWhenTenantQualifiedUrlsDisabled() throws Exception {
+
+ // Restore the server back to the initial state (tenant qualified urls disabled).
+ restoreISConfigurations();
+
+ // Create oauth app and add user.
+ ApplicationResponseModel tenant1App = createApplication(TENANT_1_DOMAIN);
+ Assert.assertNotNull(tenant1App, "OAuth app creation failed for tenant 1.");
+ tenant1UserId = createUser(TENANT_1_DOMAIN);
+
+ // Authenticate.
+ initiateAuthorizationRequest(false);
+ authenticateUser(false, TENANT_1_USER_USERNAME + "@" + TENANT_1_DOMAIN, TENANT_1_USER_PASSWORD,
+ TENANT_1_DOMAIN);
+ performConsentApproval(false, TENANT_1_DOMAIN);
+ generateAuthzCodeAccessToken(false, TENANT_1_DOMAIN);
+ introspectActiveAccessToken(TENANT_1_DOMAIN, TENANT_1_ADMIN_USERNAME, TENANT_1_ADMIN_PASSWORD);
+ }
+
+ private void changeISConfigurations() throws AutomationUtilException, IOException {
+
+ String carbonHome = Utils.getResidentCarbonHome();
+ File defaultTomlFile = getDeploymentTomlFile(carbonHome);
+ File modifiedConfigFile = new File(getISResourceLocation()
+ + File.separator + "tenant.qualified" + File.separator
+ + "tenant_qualified_url_tenanted_sessions_enabled.toml");
+ serverConfigurationManager.applyConfiguration(modifiedConfigFile, defaultTomlFile, true, true);
+ }
+
+ private void restoreISConfigurations() throws AutomationUtilException, IOException {
+
+ if (configurationsRestored) {
+ return;
+ }
+ String carbonHome = Utils.getResidentCarbonHome();
+ File defaultTomlFile = getDeploymentTomlFile(carbonHome);
+ File modifiedConfigFile = new File(getISResourceLocation()
+ + File.separator + "tenant.qualified" + File.separator
+ + "tenant_qualified_url_tenanted_sessions_default.toml");
+ serverConfigurationManager.applyConfiguration(modifiedConfigFile, defaultTomlFile, true, true);
+ configurationsRestored = true;
+ }
+
+ private void addTenant(String tenantDomain, String adminUsername, String adminPassword,
+ String adminTenantAwareUsername) throws Exception {
+
+ Owner tenantOwner = new Owner();
+ tenantOwner.setUsername(adminTenantAwareUsername);
+ tenantOwner.setPassword(adminPassword);
+ tenantOwner.setEmail(adminUsername);
+ tenantOwner.setFirstname("FirstName");
+ tenantOwner.setLastname("LastName");
+ tenantOwner.setProvisioningMethod("inline-password");
+
+ TenantModel tenantReqModel = new TenantModel();
+ tenantReqModel.setDomain(tenantDomain);
+ tenantReqModel.addOwnersItem(tenantOwner);
+ String tenantId = tenantMgtRestClient.addTenant(tenantReqModel);
+ Assert.assertNotNull(tenantId, "Tenant creation failed for " + tenantDomain);
+ }
+
+ private Tenant getTenantInfo(String tenantDomain) {
+
+ User tenantAdmin = new User();
+ if (StringUtils.equals(tenantDomain, TENANT_1_DOMAIN)) {
+ tenantAdmin.setUserName(TENANT_1_ADMIN_USERNAME);
+ tenantAdmin.setPassword(TENANT_1_ADMIN_PASSWORD);
+ } else if (StringUtils.equals(tenantDomain, TENANT_2_DOMAIN)) {
+ tenantAdmin.setUserName(TENANT_2_ADMIN_USERNAME);
+ tenantAdmin.setPassword(TENANT_2_ADMIN_PASSWORD);
+ }
+
+ Tenant tenant = new Tenant();
+ tenant.setDomain(tenantDomain);
+ tenant.setContextUser(tenantAdmin);
+ return tenant;
+ }
+
+ private OAuth2RestClient getOAuthRestClient(String tenantDomain) {
+
+ OAuth2RestClient restClient;
+ if (StringUtils.equals(tenantDomain, TENANT_1_DOMAIN)) {
+ restClient = oAuth2RestClientTenant1;
+ } else {
+ restClient = oAuth2RestClientTenant2;
+ }
+ return restClient;
+ }
+
+ private SCIM2RestClient getSCIMRestClient(String tenantDomain) {
+
+ SCIM2RestClient restClient;
+ if (StringUtils.equals(tenantDomain, TENANT_1_DOMAIN)) {
+ restClient = scim2RestClientTenant1;
+ } else {
+ restClient = scim2RestClientTenant2;
+ }
+ return restClient;
+ }
+
+ private ApplicationModel getApplicationModel() {
+
+ OpenIDConnectConfiguration oidcConfig = new OpenIDConnectConfiguration();
+ oidcConfig.setClientId(DUMMY_CLIENT_ID);
+ oidcConfig.setClientSecret(DUMMY_CLIENT_SECRET);
+ oidcConfig.setPublicClient(false);
+
+ List grantTypes = new ArrayList<>();
+ Collections.addAll(grantTypes, OAuth2Constant.OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE,
+ OAuth2Constant.OAUTH2_GRANT_TYPE_CLIENT_CREDENTIALS,
+ OAuth2Constant.OAUTH2_GRANT_TYPE_RESOURCE_OWNER,
+ OAuth2Constant.OAUTH2_GRANT_TYPE_REFRESH_TOKEN);
+ oidcConfig.setGrantTypes(grantTypes);
+
+ List callBackUrls = new ArrayList<>();
+ Collections.addAll(callBackUrls, OAuth2Constant.CALLBACK_URL);
+ oidcConfig.setCallbackURLs(callBackUrls);
+
+ InboundProtocols inboundProtocolsConfig = new InboundProtocols();
+ inboundProtocolsConfig.setOidc(oidcConfig);
+
+ ApplicationModel application = new ApplicationModel();
+ application.setName(OAuth2Constant.OAUTH_APPLICATION_NAME);
+ application.setInboundProtocolConfiguration(inboundProtocolsConfig);
+ application.setClaimConfiguration(setApplicationClaimConfig());
+
+ return application;
+ }
+
+ private ApplicationResponseModel createApplication(String tenantDomain) throws JSONException, IOException {
+
+ OAuth2RestClient restClient = getOAuthRestClient(tenantDomain);
+ String appId = restClient.createApplication(getApplicationModel());
+ return restClient.getApplication(appId);
+ }
+
+ private void deleteAllApplications(String tenantDomain) throws IOException {
+
+ OAuth2RestClient restClient = getOAuthRestClient(tenantDomain);
+ ApplicationListResponse applications = restClient.getAllApplications();
+ for (ApplicationListItem application : applications.getApplications()) {
+ restClient.deleteApplication(application.getId());
+ }
+ }
+
+ private StatusLine createApplicationWithResponse(String tenantDomain)
+ throws JSONException, IOException {
+
+ OAuth2RestClient restClient = getOAuthRestClient(tenantDomain);
+ return restClient.createApplicationWithResponse(getApplicationModel());
+ }
+
+ private String createUser(String tenantDomain) throws Exception {
+
+ SCIM2RestClient restClient = getSCIMRestClient(tenantDomain);
+ UserObject user = new UserObject();
+
+ if (StringUtils.equals(tenantDomain, TENANT_1_DOMAIN)) {
+ user.setUserName(TENANT_1_USER_USERNAME);
+ user.setPassword(TENANT_1_USER_PASSWORD);
+ user.addEmail(new Email().value(TENANT_1_USER_EMAIL));
+ } else {
+ user.setUserName(TENANT_2_USER_USERNAME);
+ user.setPassword(TENANT_2_USER_PASSWORD);
+ user.addEmail(new Email().value(TENANT_2_USER_EMAIL));
+ }
+
+ return restClient.createUser(user);
+ }
+
+ /**
+ * Playground app will initiate authorization request to IS and obtain session data key.
+ *
+ * @param tenantQualified Whether tenant qualified urls are enabled.
+ * @throws IOException IOException.
+ */
+ private void initiateAuthorizationRequest(boolean tenantQualified) throws IOException {
+
+ List urlParameters = getOIDCInitiationRequestParams(tenantQualified);
+ HttpResponse response = sendPostRequestWithParameters(client, urlParameters,
+ OAuth2Constant.AUTHORIZED_USER_URL);
+ Assert.assertNotNull(response, "Authorization response is null");
+
+ Header locationHeader = response.getFirstHeader(OAuth2Constant.HTTP_RESPONSE_HEADER_LOCATION);
+ Assert.assertNotNull(locationHeader, "Authorization response header is null.");
+
+ EntityUtils.consume(response.getEntity());
+ response = sendGetRequest(client, locationHeader.getValue());
+ sessionDataKey = Utils.extractDataFromResponse(response, CommonConstants.SESSION_DATA_KEY, 1);
+ EntityUtils.consume(response.getEntity());
+ }
+
+ private List getOIDCInitiationRequestParams(boolean tenantQualified) {
+
+ List urlParameters = new ArrayList<>();
+ urlParameters.add(new BasicNameValuePair("grantType", OAuth2Constant.OAUTH2_GRANT_TYPE_CODE));
+ urlParameters.add(new BasicNameValuePair("consumerKey", DUMMY_CLIENT_ID));
+ urlParameters.add(new BasicNameValuePair("callbackurl", OAuth2Constant.CALLBACK_URL));
+ if (tenantQualified) {
+ urlParameters.add(new BasicNameValuePair("authorizeEndpoint", TENANT_1_AUTHORIZE_URL));
+ } else {
+ urlParameters.add(new BasicNameValuePair("authorizeEndpoint", OAuth2Constant.AUTHORIZE_ENDPOINT_URL));
+ }
+ urlParameters.add(new BasicNameValuePair("authorize", OAuth2Constant.AUTHORIZE_PARAM));
+ urlParameters.add(new BasicNameValuePair("scope", OAuth2Constant.OAUTH2_SCOPE_OPENID + " " +
+ OAuth2Constant.OAUTH2_SCOPE_EMAIL));
+
+ return urlParameters;
+ }
+
+ /**
+ * Provide user credentials and authenticate to the system.
+ *
+ * @param tenantQualified Whether tenant qualified urls are enabled.
+ * @param username Username of the user.
+ * @param password Password of the user.
+ * @param tenantDomain Tenant domain.
+ * @throws IOException IOException.
+ */
+ private void authenticateUser(boolean tenantQualified, String username, String password, String tenantDomain)
+ throws Exception {
+
+ // Pass user credentials to commonauth endpoint and authenticate the user.
+ HttpResponse response;
+ if (tenantQualified) {
+ response = sendLoginPostForCustomUsers(client, sessionDataKey, username, password, tenantDomain);
+ } else {
+ response = sendLoginPostForCustomUsers(client, sessionDataKey, username, password);
+ }
+ Assert.assertNotNull(response, "OIDC login request response is null.");
+
+ Header locationHeader = response.getFirstHeader(OAuth2Constant.HTTP_RESPONSE_HEADER_LOCATION);
+ Assert.assertNotNull(locationHeader, "OIDC login response header is null.");
+ EntityUtils.consume(response.getEntity());
+
+ // Get the sessionDatakeyConsent from the redirection after authenticating the user.
+ response = sendGetRequest(client, locationHeader.getValue());
+ Map keyPositionMap = new HashMap<>(1);
+ keyPositionMap.put("name=\"sessionDataKeyConsent\"", 1);
+ List keyValues = DataExtractUtil
+ .extractSessionConsentDataFromResponse(response, keyPositionMap);
+ Assert.assertNotNull(keyValues, "SessionDataKeyConsent keyValues map is null.");
+ sessionDataKeyConsent = keyValues.get(0).getValue();
+ Assert.assertNotNull(sessionDataKeyConsent, "sessionDataKeyConsent is null.");
+ EntityUtils.consume(response.getEntity());
+ }
+
+ /**
+ * Approve the consent.
+ *
+ * @param tenantQualified Whether tenant qualified urls are enabled.
+ * @param tenantDomain Tenant domain.
+ * @throws IOException IOException.
+ */
+ private void performConsentApproval(boolean tenantQualified, String tenantDomain) throws IOException {
+
+ HttpResponse response;
+ if (tenantQualified) {
+ response = sendApprovalPostWithConsent(client, sessionDataKeyConsent, null, tenantDomain);
+ } else {
+ response = sendApprovalPostWithConsent(client, sessionDataKeyConsent, null);
+ }
+ Assert.assertNotNull(response, "OIDC consent approval request response is null.");
+ Header locationHeader = response.getFirstHeader(OAuth2Constant.HTTP_RESPONSE_HEADER_LOCATION);
+ Assert.assertNotNull(locationHeader, "OIDC consent approval request location header is null.");
+ EntityUtils.consume(response.getEntity());
+
+ // Get authorization code flow.
+ String[] queryParams = new URL(locationHeader.getValue()).getQuery().split("&");
+ Assert.assertNotEquals(queryParams.length, 0, "Authorization code not received.");
+ for (String param : queryParams) {
+ if (param.contains(OAuth2Constant.OAUTH2_GRANT_TYPE_CODE)) {
+ authorizationCode = param.split("=")[1];
+ }
+ }
+ Assert.assertNotNull(authorizationCode, "Authorization code not received.");
+ }
+
+ /**
+ * Exchange authorization code and get access token.
+ *
+ * @param tenantQualified Whether tenant qualified urls are enabled.
+ * @param tenantDomain Tenant domain.
+ * @throws Exception IOException.
+ */
+ private void generateAuthzCodeAccessToken(boolean tenantQualified, String tenantDomain) throws Exception {
+
+ List urlParameters = new ArrayList<>();
+ urlParameters.add(new BasicNameValuePair(OAuth2Constant.GRANT_TYPE_NAME,
+ OAuth2Constant.OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE));
+ urlParameters.add(new BasicNameValuePair(OAuth2Constant.OAUTH2_REDIRECT_URI, OAuth2Constant.CALLBACK_URL));
+ urlParameters.add(new BasicNameValuePair(OAuth2Constant.AUTHORIZATION_CODE_NAME, authorizationCode));
+
+ String url;
+ if (tenantQualified) {
+ url = OAuth2Constant.TENANT_TOKEN_ENDPOINT.replace(OAuth2Constant.TENANT_PLACEHOLDER, tenantDomain);
+ } else {
+ url = OAuth2Constant.ACCESS_TOKEN_ENDPOINT;
+ }
+ JSONObject jsonResponse = responseObject(url, urlParameters, DUMMY_CLIENT_ID, DUMMY_CLIENT_SECRET);
+ Assert.assertNotNull(jsonResponse.get(OAuth2Constant.ACCESS_TOKEN), "Access token is null.");
+ Assert.assertNotNull(jsonResponse.get(OAuth2Constant.REFRESH_TOKEN), "Refresh token is null.");
+ accessToken = (String) jsonResponse.get(OAuth2Constant.ACCESS_TOKEN);
+ }
+
+ /**
+ * Introspect the obtained access token and it should be an active token.
+ *
+ * @param tenantDomain Tenant domain.
+ * @throws Exception Exception.
+ */
+ private void introspectActiveAccessToken(String tenantDomain, String adminUsername, String adminPassword)
+ throws Exception {
+
+ String url = OAuth2Constant.TENANT_INTROSPECT_ENDPOINT.replace(
+ OAuth2Constant.TENANT_PLACEHOLDER, tenantDomain);
+ JSONObject object = introspectTokenWithTenant(client, accessToken, url, adminUsername, adminPassword);
+ Assert.assertEquals(object.get("active"), true);
+ }
+
+ /**
+ * Build post request and return json response object.
+ *
+ * @param endpoint Endpoint.
+ * @param postParameters postParameters.
+ * @param key Basic authentication key.
+ * @param secret Basic authentication secret.
+ * @return JSON object of the response.
+ * @throws Exception Exception.
+ */
+ private JSONObject responseObject(String endpoint, List postParameters, String key, String secret)
+ throws Exception {
+
+ HttpPost httpPost = new HttpPost(endpoint);
+ httpPost.setHeader("Authorization", "Basic " + getBase64EncodedString(key, secret));
+ httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded");
+ httpPost.setEntity(new UrlEncodedFormEntity(postParameters));
+ HttpResponse response = client.execute(httpPost);
+
+ String responseString = EntityUtils.toString(response.getEntity(), "UTF-8");
+ EntityUtils.consume(response.getEntity());
+ JSONParser parser = new JSONParser();
+ JSONObject json = (JSONObject) parser.parse(responseString);
+ if (json == null) {
+ throw new Exception("Error occurred while getting the response.");
+ }
+
+ return json;
+ }
+}
diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/application/management/v1/model/ApplicationListItem.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/application/management/v1/model/ApplicationListItem.java
index 8f291e49d9e..cbc0983a98e 100644
--- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/application/management/v1/model/ApplicationListItem.java
+++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/application/management/v1/model/ApplicationListItem.java
@@ -30,6 +30,7 @@ public class ApplicationListItem {
private String description;
private String image;
private String accessUrl;
+ private String clientId;
@XmlType(name="AccessEnum")
@XmlEnum(String.class)
@@ -192,7 +193,24 @@ public void setSelf(String self) {
this.self = self;
}
+ public ApplicationListItem clientId(String clientId) {
+ this.clientId = clientId;
+ return this;
+ }
+
+ @ApiModelProperty(example = "clientId", value = "")
+ @JsonProperty("clientId")
+ @Valid
+ public String getClientId() {
+
+ return clientId;
+ }
+
+ public void setClientId(String clientId) {
+
+ this.clientId = clientId;
+ }
@Override
public boolean equals(java.lang.Object o) {
diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java
index 1117de8bf03..00e8978e71b 100644
--- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java
+++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java
@@ -22,6 +22,7 @@
import io.restassured.http.ContentType;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.Header;
+import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.message.BasicHeader;
import org.apache.http.util.EntityUtils;
@@ -30,6 +31,7 @@
import org.wso2.carbon.automation.engine.context.beans.Tenant;
import org.wso2.carbon.utils.StringUtils;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
+import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.ApplicationListItem;
import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.ApplicationListResponse;
import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.ApplicationModel;
import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.ApplicationPatchModel;
@@ -40,6 +42,7 @@
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
+import java.util.List;
public class OAuth2RestClient extends RestBaseClient {
@@ -74,6 +77,21 @@ public String createApplication(ApplicationModel application) throws IOException
}
}
+ /**
+ * Create an Application.
+ *
+ * @param application Application Model with application creation details.
+ * @return Application creation response.
+ */
+ public StatusLine createApplicationWithResponse(ApplicationModel application) throws IOException, JSONException {
+
+ String jsonRequest = toJSONString(application);
+ try (CloseableHttpResponse response = getResponseOfHttpPost(applicationManagementApiBasePath, jsonRequest,
+ getHeaders())) {
+ return response.getStatusLine();
+ }
+ }
+
/**
* Get Application details
*
@@ -91,6 +109,27 @@ public ApplicationResponseModel getApplication(String appId) throws IOException
}
}
+ /**
+ * Get Application details by client id.
+ *
+ * @param clientId Client id of the application.
+ * @return Application list.
+ * @throws IOException Error when getting the response.
+ */
+ public List getApplicationsByClientId(String clientId) throws IOException {
+
+ String endPointUrl = applicationManagementApiBasePath + "?filter=clientId eq " + clientId;
+ endPointUrl = endPointUrl.replace(" ", "%20");
+
+ try (CloseableHttpResponse response = getResponseOfHttpGet(endPointUrl, getHeaders())) {
+ String responseBody = EntityUtils.toString(response.getEntity());
+
+ ObjectMapper jsonWriter = new ObjectMapper(new JsonFactory());
+ ApplicationListResponse applications = jsonWriter.readValue(responseBody, ApplicationListResponse.class);
+ return applications.getApplications();
+ }
+ }
+
/**
* Update an existing application
*
diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/user/export/UserInfoExportTestCase.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/user/export/UserInfoExportTestCase.java
index 7b5be0134e8..57ca37dce9a 100644
--- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/user/export/UserInfoExportTestCase.java
+++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/user/export/UserInfoExportTestCase.java
@@ -51,11 +51,10 @@ public class UserInfoExportTestCase extends ISIntegrationTest {
private static final String PI_INFO = "pi-info/";
private static final String ME = "me";
private static final String RESOURCE_PATH = "/api/identity/user/v1.0/";
- private static final String USERNAME_CLAIM_URI = "http://wso2.org/claims/username";
+ private static final String GROUPS_ATTRIBUTE = "groups";
private HttpClient client;
private String username;
- private String tenantAwareUsername;
private String password;
private String tenant;
@@ -64,7 +63,6 @@ public UserInfoExportTestCase(TestUserMode userMode) throws Exception {
AutomationContext context = new AutomationContext("IDENTITY", userMode);
this.username = context.getContextTenant().getTenantAdmin().getUserName();
- this.tenantAwareUsername = context.getContextTenant().getTenantAdmin().getUserNameWithoutDomain();
this.password = context.getContextTenant().getTenantAdmin().getPassword();
this.tenant = context.getContextTenant().getDomain();
}
@@ -97,14 +95,13 @@ public void testExportUserInfo() throws IOException {
Object responseObj = JSONValue.parse(rd);
EntityUtils.consume(response.getEntity());
- Object basicObj = ((JSONObject) responseObj).get("basic");
- if (basicObj == null) {
+ Object userProfileObj = ((JSONObject) responseObj).get("user_profile");
+ if (userProfileObj == null) {
Assert.fail();
} else {
- JSONObject basic = (JSONObject) basicObj;
- String username = basic.get(USERNAME_CLAIM_URI).toString();
- //TODO tenant aware username is coming. is this okay?
- Assert.assertEquals(username, this.tenantAwareUsername);
+ JSONObject userProfile = (JSONObject) userProfileObj;
+ String groups = userProfile.get(GROUPS_ATTRIBUTE).toString();
+ Assert.assertNotNull(groups);
}
}
@@ -121,10 +118,9 @@ public void testExportUserInfoMe() throws IOException {
Object responseObj = JSONValue.parse(rd);
EntityUtils.consume(response.getEntity());
- JSONObject basic = (JSONObject)((JSONObject) responseObj).get("basic");
- String username = basic.get(USERNAME_CLAIM_URI).toString();
- //TODO tenant aware username is coming. is this okay?
- Assert.assertEquals(username, this.tenantAwareUsername);
+ JSONObject userProfile = (JSONObject)((JSONObject) responseObj).get("user_profile");
+ String groups = userProfile.get(GROUPS_ATTRIBUTE).toString();
+ Assert.assertNotNull(groups);
}
private String getPiInfoPath() {
diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java
index 286e6a9eebe..67434f4c365 100644
--- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java
+++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java
@@ -120,6 +120,14 @@ public final class OAuth2Constant {
public static final String RESPONSE_TYPE_CODE_ID_TOKEN = "code id_token";
+ // Tenanted urls.
+ public final static String TENANT_PLACEHOLDER = "";
+ public final static String TENANT_COMMON_AUTH_URL = "https://localhost:9853/t//commonauth";
+ public final static String TENANT_APPROVAL_URL = "https://localhost:9853/t//oauth2/authorize";
+ public final static String TENANT_TOKEN_ENDPOINT = "https://localhost:9853/t//oauth2/token";
+ public static final String TENANT_INTROSPECT_ENDPOINT =
+ "https://localhost:9853/t//oauth2/introspect";
+
public static final class PlaygroundAppPaths {
public static final String callBackPath = "/oauth2client";
diff --git a/modules/integration/tests-integration/tests-backend/src/test/resources/artifacts/IS/tenant.qualified/tenant_qualified_url_tenanted_sessions_default.toml b/modules/integration/tests-integration/tests-backend/src/test/resources/artifacts/IS/tenant.qualified/tenant_qualified_url_tenanted_sessions_default.toml
new file mode 100644
index 00000000000..2a6ee115a7e
--- /dev/null
+++ b/modules/integration/tests-integration/tests-backend/src/test/resources/artifacts/IS/tenant.qualified/tenant_qualified_url_tenanted_sessions_default.toml
@@ -0,0 +1,39 @@
+[server]
+hostname = "localhost"
+node_ip = "127.0.0.1"
+base_path = "https://$ref{server.hostname}:${carbon.management.port}"
+
+[super_admin]
+username = "admin"
+password = "admin"
+create_admin_account = true
+
+[user_store]
+type = "database_unique_id"
+
+[database.identity_db]
+driver = "$env{IDENTITY_DATABASE_DRIVER}"
+url = "$env{IDENTITY_DATABASE_URL}"
+username = "$env{IDENTITY_DATABASE_USERNAME}"
+password = "$env{IDENTITY_DATABASE_PASSWORD}"
+
+[database.shared_db]
+driver = "$env{SHARED_DATABASE_DRIVER}"
+url = "$env{SHARED_DATABASE_URL}"
+username = "$env{SHARED_DATABASE_USERNAME}"
+password = "$env{SHARED_DATABASE_PASSWORD}"
+
+[keystore.primary]
+file_name = "wso2carbon.jks"
+password = "wso2carbon"
+
+[truststore]
+file_name = "client-truststore.jks"
+password = "wso2carbon"
+type = "JKS"
+
+[account_recovery.endpoint.auth]
+hash = "66cd9688a2ae068244ea01e70f0e230f5623b7fa4cdecb65070a09ec06452262"
+
+[identity.auth_framework.endpoint]
+app_password = "dashboard"
diff --git a/modules/integration/tests-integration/tests-backend/src/test/resources/artifacts/IS/tenant.qualified/tenant_qualified_url_tenanted_sessions_enabled.toml b/modules/integration/tests-integration/tests-backend/src/test/resources/artifacts/IS/tenant.qualified/tenant_qualified_url_tenanted_sessions_enabled.toml
new file mode 100644
index 00000000000..8dfe125c8c8
--- /dev/null
+++ b/modules/integration/tests-integration/tests-backend/src/test/resources/artifacts/IS/tenant.qualified/tenant_qualified_url_tenanted_sessions_enabled.toml
@@ -0,0 +1,43 @@
+[server]
+hostname = "localhost"
+node_ip = "127.0.0.1"
+base_path = "https://$ref{server.hostname}:${carbon.management.port}"
+
+[super_admin]
+username = "admin"
+password = "admin"
+create_admin_account = true
+
+[user_store]
+type = "database_unique_id"
+
+[database.identity_db]
+driver = "$env{IDENTITY_DATABASE_DRIVER}"
+url = "$env{IDENTITY_DATABASE_URL}"
+username = "$env{IDENTITY_DATABASE_USERNAME}"
+password = "$env{IDENTITY_DATABASE_PASSWORD}"
+
+[database.shared_db]
+driver = "$env{SHARED_DATABASE_DRIVER}"
+url = "$env{SHARED_DATABASE_URL}"
+username = "$env{SHARED_DATABASE_USERNAME}"
+password = "$env{SHARED_DATABASE_PASSWORD}"
+
+[keystore.primary]
+file_name = "wso2carbon.jks"
+password = "wso2carbon"
+
+[truststore]
+file_name = "client-truststore.jks"
+password = "wso2carbon"
+type = "JKS"
+
+[account_recovery.endpoint.auth]
+hash = "66cd9688a2ae068244ea01e70f0e230f5623b7fa4cdecb65070a09ec06452262"
+
+[identity.auth_framework.endpoint]
+app_password = "dashboard"
+
+[tenant_context]
+enable_tenant_qualified_urls = true
+enable_tenanted_sessions = true
diff --git a/modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml b/modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml
index feb491d40aa..88cd04b199e 100644
--- a/modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml
+++ b/modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml
@@ -346,6 +346,7 @@
+
diff --git a/modules/p2-profile-gen/pom.xml b/modules/p2-profile-gen/pom.xml
index 1f3ae282cf1..217c07bf683 100644
--- a/modules/p2-profile-gen/pom.xml
+++ b/modules/p2-profile-gen/pom.xml
@@ -263,6 +263,9 @@
org.wso2.carbon.extension.identity.authenticator.outbound.emailotp:org.wso2.carbon.identity.authenticator.emailotp.feature:${authenticator.emailotp.version}
+
+ org.wso2.carbon.extension.identity.oauth.addons:org.wso2.carbon.identity.oauth2.token.handler.clientauth.jwt.feature:${identity.oauth.addons.version}
+
org.wso2.carbon.identity.user.ws:org.wso2.carbon.um.ws.service.feature:${identity.user.ws.version}
@@ -1102,6 +1105,10 @@
org.wso2.carbon.identity.authenticator.emailotp.feature.group
${authenticator.emailotp.version}
+
+ org.wso2.carbon.identity.oauth2.token.handler.clientauth.jwt.feature.group
+ ${identity.oauth.addons.version}
+
org.wso2.carbon.identity.application.authenticator.oidc.server.feature.group
@@ -1673,6 +1680,10 @@
org.wso2.carbon.identity.authenticator.emailotp.feature.group
${authenticator.emailotp.version}
+
+ org.wso2.carbon.identity.oauth2.token.handler.clientauth.jwt.feature.group
+ ${identity.oauth.addons.version}
+
org.wso2.carbon.extension.identity.verification.provider.feature.group
${identity.verification.version}
@@ -2148,6 +2159,10 @@
org.wso2.carbon.identity.authenticator.emailotp.feature.group
${authenticator.emailotp.version}
+
+ org.wso2.carbon.identity.oauth2.token.handler.clientauth.jwt.feature.group
+ ${identity.oauth.addons.version}
+
org.wso2.carbon.extension.identity.verification.provider.feature.group
${identity.verification.version}
diff --git a/pom.xml b/pom.xml
index e7e5d225941..5a7aa3dd533 100755
--- a/pom.xml
+++ b/pom.xml
@@ -2049,6 +2049,12 @@
org.wso2.carbon.extension.identity.verification.ui
${identity.verification.version}
+
+ org.wso2.carbon.extension.identity.oauth.addons
+ org.wso2.carbon.identity.oauth2.token.handler.clientauth.jwt
+ ${identity.oauth.addons.version}
+
+
org.wso2.msf4j
msf4j-core
@@ -2292,7 +2298,7 @@
- 5.25.356
+ 5.25.375
[5.14.67, 6.0.0]
@@ -2303,18 +2309,18 @@
2.5.2
- 1.8.73
+ 1.8.76
5.8.5
5.5.0
5.5.0
- 1.8.21
+ 1.8.22
5.11.23
- 6.11.127
+ 6.11.130
5.9.5
5.10.16
5.7.4
@@ -2340,7 +2346,7 @@
1.8.12
- 1.7.16
+ 1.7.17
@@ -2372,7 +2378,7 @@
2.0.3
- 6.7.23
+ 6.7.26
5.3.39
5.4.2
@@ -2399,8 +2405,8 @@
1.0.14
1.0.0
- 1.3.72
- 1.0.63
+ 1.3.76
+ 1.0.67
1.1.21
1.1.9
@@ -2408,12 +2414,12 @@
0.1.4
- 1.0.5
+ 1.0.7
2.0.13
1.3.22
- 1.2.84
+ 1.2.86
5.5.9
5.5.7
@@ -2423,9 +2429,9 @@
1.2.36
- 2.0.12
- 2.0.6
- 2.0.5
+ 2.0.24
+ 2.0.9
+ 2.0.7
1.6.373