Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pre auth and tenant switch improvement for central deployment related authentication #7291

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changeset/lazy-elephants-refuse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"@wso2is/admin.tenants.v1": minor
"@wso2is/admin.administration.v1": minor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's no changes to this administrators package in this pr, hence we can remove it I guess.

"@wso2is/admin.base.v1": minor
"@wso2is/admin.core.v1": minor
"@wso2is/myaccount": minor
"@wso2is/console": minor
"@wso2is/core": minor
"@wso2is/i18n": minor
---

Introduce deployment units to tenant dropdown and tenant creation.
112 changes: 98 additions & 14 deletions apps/console/src/auth.html
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@
var proxyContextPathGlobal = startupConfig.proxyContextPathGlobal;
var superTenantGlobal = startupConfig.superTenant;
var tenantPrefixGlobal = startupConfig.tenantPrefix;
var isCentralDeploymentEnabled = startupConfig.enableCentralDeployment;
var isAdaptiveAuthenticationAvailable = true;
var isOrganizationManagementEnabled = true;

Expand All @@ -179,13 +180,14 @@
sessionStorage.setItem("auth_callback_url_console", authCallbackUrl);
}

var serverOrigin = startupConfig.serverUrl;
var serverOrigin = isCentralDeploymentEnabled ? startupConfig.centralServerUrl : startupConfig.serverUrl;
var deploymentUnitServerOrigin = startupConfig.serverUrl;
var authorizationCode = urlParams.get("code");
var authSessionState = urlParams.get("session_state");
var authIdPs = urlParams.get("AuthenticatedIdPs");

function authenticateWithSDK() {
if(!authorizationCode) {
if (!authorizationCode || isCentralDeploymentEnabled) {
function getTenantName() {
var path = window.location.pathname;
var pathChunks = path.split("/");
Expand Down Expand Up @@ -224,6 +226,27 @@
return serverOrigin + getTenantPath(tenantDomain);
}

/**
* Get the deployment unit API path.
*
* @param {string} path - Path to be appended to the API path.
* @param {string} tenantDomain - Tenant domain.
* @returns {string} Constructed API path.
*/
function getDeploymentUnitApiPath(path, tenantDomain) {
if (!tenantDomain) {
if (startupConfig.superTenantProxy) {
tenantDomain = startupConfig.superTenantProxy;
} else {
tenantDomain = startupConfig.superTenant;
}
}
if (path) {
return deploymentUnitServerOrigin + getTenantPath(tenantDomain) + path;
}
return deploymentUnitServerOrigin + getTenantPath(tenantDomain);
}

/**
* Get the organization name.
*
Expand Down Expand Up @@ -262,12 +285,25 @@
// When there's no proxy context path, the IS server returns "null".
var contextPath = (!proxyContextPathGlobal || proxyContextPathGlobal === "null") ? "" : "/" + proxyContextPathGlobal;

if (isCentralDeploymentEnabled) {
return applicationDomain
}
if (getTenantName() === startupConfig.superTenant) {
return applicationDomain.replace(/\/+$/, '') + contextPath
+ "<%= htmlWebpackPlugin.options.basename ? '/' + htmlWebpackPlugin.options.basename : ''%>";
}

return applicationDomain.replace(/\/+$/, '') + contextPath + getTenantPath()
return applicationDomain.replace(/\/+$/, '') + getTenantPath()
+ "<%= htmlWebpackPlugin.options.basename ? '/' + htmlWebpackPlugin.options.basename : ''%>";
}

/**
* Construct the sign-in redirect URL for deployment unit.
*
* @returns {string} Constructed URL
*/
function deploymentUnitSignInRedirectURL(tenantDomain) {
return `${applicationDomain}/${tenantPrefixGlobal}/${tenantDomain}`
+ "<%= htmlWebpackPlugin.options.basename ? '/' + htmlWebpackPlugin.options.basename : ''%>";
}

Expand Down Expand Up @@ -333,7 +369,22 @@
}

var auth = AsgardeoAuth.AsgardeoSPAClient.getInstance();
if (isCentralDeploymentEnabled) {
auth = AsgardeoAuth.AsgardeoSPAClient.getInstance("primary");
}

var endpoints = {
authorizationEndpoint: getApiPath("/oauth2/authorize"),
clockTolerance: 300,
jwksEndpointURL: isCentralDeploymentEnabled ? ("/oauth2/jwks") : undefined,
logoutEndpointURL: getApiPath("/oidc/logout"),
oidcSessionIFrameEndpointURL: getApiPath("/oidc/checksession"),
tokenEndpointURL: undefined,
tokenRevocationEndpointURL: isCentralDeploymentEnabled ? ("/oauth2/revoke") : undefined
};
if (isCentralDeploymentEnabled) {
endpoints.issuer = `${serverOrigin}/oauth2/token`;
}
var authConfig = {
signInRedirectURL: signInRedirectURL(),
signOutRedirectURL: getSignOutRedirectURL(),
Expand All @@ -344,15 +395,7 @@
storage: "webWorker",
disableTrySignInSilently: true,
enableOIDCSessionManagement: false,
endpoints: {
authorizationEndpoint: getApiPath("/oauth2/authorize"),
clockTolerance: 300,
jwksEndpointURL: undefined,
logoutEndpointURL: getApiPath("/oidc/logout"),
oidcSessionIFrameEndpointURL: getApiPath("/oidc/checksession"),
tokenEndpointURL: undefined,
tokenRevocationEndpointURL: undefined
},
endpoints: endpoints,
enablePKCE: true
}

Expand Down Expand Up @@ -385,13 +428,54 @@
sessionStorage.setItem("auth_callback_url_console", authCallbackUrl);
}

auth.signIn(getAuthParams({}));
if (isCentralDeploymentEnabled) {
if (authorizationCode) {
auth.signIn({callOnlyOnRedirect: true})
.then((response) => {
auth.getDecodedIDToken().then((token) => {
var defaultTenant = token["default_tenant"]
var authConfigRegion = {
signInRedirectURL: deploymentUnitSignInRedirectURL(defaultTenant),
signOutRedirectURL: getSignOutRedirectURL(),
clientID: "<%= htmlWebpackPlugin.options.clientID %>",
baseUrl: getDeploymentUnitApiPath(defaultTenant),
responseMode: "query",
scope: ["openid SYSTEM profile"],
storage: "webWorker",
disableTrySignInSilently: false,
enableOIDCSessionManagement: false,
endpoints: {
authorizationEndpoint: getDeploymentUnitApiPath("/oauth2/authorize", defaultTenant),
clockTolerance: 300,
jwksEndpointURL: getDeploymentUnitApiPath("/oauth2/jwks", defaultTenant),
logoutEndpointURL: getDeploymentUnitApiPath("/oidc/logout", defaultTenant),
oidcSessionIFrameEndpointURL: getDeploymentUnitApiPath("/oidc/checksession", defaultTenant),
tokenEndpointURL: getDeploymentUnitApiPath("/oauth2/token", defaultTenant),
tokenRevocationEndpointURL: getDeploymentUnitApiPath("/oauth2/revoke", defaultTenant),
issuer: getDeploymentUnitApiPath("/oauth2/token", defaultTenant)
},
enablePKCE: true
}
var authSecondary = AsgardeoAuth.AsgardeoSPAClient.getInstance("secondary");

authSecondary.initialize(authConfigRegion);

authSecondary.signIn({prompt: "login",fidp: "PlatformIDP"})

})
})
} else {
auth.signIn(getAuthParams({}))
}
} else {
auth.signIn(getAuthParams({}));
}
}
}
}
</script>
<script>
if(!authorizationCode) {
if(!authorizationCode || isCentralDeploymentEnabled) {
var authSPAJS = document.createElement("script");
var authScriptSrc = "<%= htmlWebpackPlugin.options.basename ? '/' + htmlWebpackPlugin.options.basename + '/auth-spa-3.1.2.min.js' : '/auth-spa-3.1.2.min.js'%>";

Expand Down
1 change: 1 addition & 0 deletions apps/console/src/init/app-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ export const AppUtils: any = (function() {
appBase: _config.appBaseName,
appBaseNameForHistoryAPI: this.constructAppBaseNameForHistoryAPI(),
appBaseWithTenant: this.getAppBaseWithTenantAndOrganization(),
centralDeploymentEnabled: _config.centralDeploymentEnabled,
clientID: this.getClientId(),
clientOrigin: _config.clientOrigin,
clientOriginWithTenant: this.getClientOriginWithTenant(),
Expand Down
1 change: 1 addition & 0 deletions apps/myaccount/src/configs/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export class Config {
appHomePath: window["AppUtils"]?.getConfig()?.routes?.home,
appLoginPath: window["AppUtils"]?.getConfig()?.routes?.login,
appLogoutPath: window["AppUtils"]?.getConfig()?.routes?.logout,
centralDeploymentEnabled: window["AppUtils"]?.getConfig()?.centralDeploymentEnabled,
clientHost: window["AppUtils"]?.getConfig()?.clientOriginWithTenant,
clientID: window["AppUtils"]?.getConfig()?.clientID,
clientOrigin: window["AppUtils"]?.getConfig()?.clientOrigin,
Expand Down
1 change: 1 addition & 0 deletions features/admin.base.v1/utils/app-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ export const AppUtils: any = (function() {
appBase: _config.appBaseName,
appBaseNameForHistoryAPI: this.constructAppBaseNameForHistoryAPI(),
appBaseWithTenant: this.getAppBaseWithTenantAndOrganization(),
centralDeploymentEnabled: _config.centralDeploymentEnabled,
clientID: this.getClientId(),
clientOrigin: _config.clientOrigin,
clientOriginWithTenant: this.getClientOriginWithTenant(),
Expand Down
1 change: 1 addition & 0 deletions features/admin.core.v1/configs/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export class Config {
appHomePath: window[ "AppUtils" ]?.getConfig()?.routes?.home,
appLoginPath: window[ "AppUtils" ]?.getConfig()?.routes?.login,
appLogoutPath: window[ "AppUtils" ]?.getConfig()?.routes?.logout,
centralDeploymentEnabled: window[ "AppUtils" ]?.getConfig()?.routes?.centralDeploymentEnabled,
Copy link
Contributor

@pavinduLakshan pavinduLakshan Feb 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In getconfig function in console/src/init/app-utils, this value is returned in the config object as a first level property, not as a nested property of routes object. So ideally the config value should be taken as follows.

Suggested change
centralDeploymentEnabled: window[ "AppUtils" ]?.getConfig()?.routes?.centralDeploymentEnabled,
centralDeploymentEnabled: window[ "AppUtils" ]?.getConfig()?.centralDeploymentEnabled,

clientHost: window[ "AppUtils" ]?.getConfig()?.clientOriginWithTenant,
clientID: window[ "AppUtils" ]?.getConfig()?.clientID,
clientOrigin: window[ "AppUtils" ]?.getConfig()?.clientOrigin,
Expand Down
2 changes: 2 additions & 0 deletions features/admin.core.v1/store/reducers/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export const commonConfigReducerInitialState: CommonConfigReducerStateInterface<
appHomePath: "",
appLoginPath: "",
appLogoutPath: "",
centralDeploymentEnabled: undefined,
clientHost: "",
clientID: "",
clientOrigin: "",
Expand Down Expand Up @@ -108,6 +109,7 @@ export const commonConfigReducerInitialState: CommonConfigReducerStateInterface<
dcrConfiguration: "",
deleteSecret: "",
deleteSecretType: "",
deploymentUnit: "",
entitlementPoliciesApi: "",
entitlementPolicyCombiningAlgorithmApi: "",
entitlementPolicyPublishApi: "",
Expand Down
27 changes: 25 additions & 2 deletions features/admin.tenants.v1/api/tenants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { store } from "@wso2is/admin.core.v1/store";
import { OrganizationType } from "@wso2is/admin.organizations.v1/constants";
import { HttpMethods } from "@wso2is/core/models";
import { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios";
import { TenantRequestResponse } from "../models";
import { DeploymentUnit, DeploymentUnitResponse, TenantRequestResponse } from "../models";

const getDomainQueryParam = (): string => {
const tenantDomain: string = store.getState().auth.tenantDomain;
Expand All @@ -47,9 +47,10 @@ const httpClient: HttpClientInstance = AsgardeoSPAClient.getInstance()
*
* @param tenantName - new tenant name
*/
export const addNewTenant = (tenantName: string): Promise<AxiosResponse> => {
export const addNewTenant = (tenantName: string, deploymentUnit?: DeploymentUnit): Promise<AxiosResponse> => {
const requestConfig: AxiosRequestConfig = {
data: {
deploymentUnit: deploymentUnit?.name,
domain: tenantName
},
headers: {
Expand Down Expand Up @@ -160,3 +161,25 @@ export const getAssociatedTenants = (
return Promise.reject(error?.response?.data);
});
};

/**
* Get the deployment units.
*
* @returns - A promise that resolves with the Deployment response object.
*/
export const getDeploymentUnits = (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we use SWR for http get requests. Therefore, shall we refactor this method to a separate hook that utilize useRequest hook?

): Promise<DeploymentUnitResponse> => {

const requestConfig: AxiosRequestConfig = {
method: HttpMethods.GET,
url: store.getState().config.endpoints.deploymentUnit + getDomainQueryParam()
};

return httpClient(requestConfig)
.then((response: AxiosResponse) => {
return Promise.resolve(response?.data);
})
.catch((error: AxiosError) => {
return Promise.reject(error?.response?.data);
});
};
15 changes: 11 additions & 4 deletions features/admin.tenants.v1/components/add-modal/add-tenant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { useDispatch } from "react-redux";
import { Dispatch } from "redux";
import { Grid, Modal } from "semantic-ui-react";
import { addNewTenant, checkDuplicateTenants } from "../../api";
import { DeploymentUnit } from "../../models";
import { handleTenantSwitch } from "../../utils";
import { AddTenantWizardForm, AddTenantWizardFormValuesInterface } from "../forms";

Expand All @@ -38,6 +39,7 @@ import { AddTenantWizardForm, AddTenantWizardFormValuesInterface } from "../form
interface AddTenantWizardPropsInterface extends TestableComponentInterface {
openModal: boolean;
onCloseHandler: () => void;
deploymentUnits: DeploymentUnit[];
}

/**
Expand All @@ -52,6 +54,7 @@ export const AddTenantWizard: FunctionComponent<AddTenantWizardPropsInterface> =

const {
openModal,
deploymentUnits,
onCloseHandler,
[ "data-testid" ]: testId
} = props;
Expand Down Expand Up @@ -99,7 +102,10 @@ export const AddTenantWizard: FunctionComponent<AddTenantWizardPropsInterface> =
.catch((error: AxiosError) => {
if (error.response.status == 404) {
// Proceed to tenant creation if tenant does not exist.
addTenant(submissionValue.tenantName);
addTenant(
submissionValue.tenantName,
submissionValue.deploymentUnitJson ?? JSON.parse(submissionValue.deploymentUnitJson)
);
} else {
setIsNewTenantLoading(false);
setAlert({
Expand Down Expand Up @@ -134,16 +140,17 @@ export const AddTenantWizard: FunctionComponent<AddTenantWizardPropsInterface> =
setTenantDuplicate={ setIsTenantDuplicate }
isCheckingTenantExistence={ isCheckingTenantExistence }
setCheckingTenantExistence={ setCheckingTenantExistence }
deploymentUnits={ deploymentUnits }
/>
),
icon: "", // TODO: Add icon
title: t("extensions:manage.features.tenant.wizards.addTenant.heading")
} ];

const addTenant = (tenantName: string): void => {
const addTenant = (tenantName: string, deploymentUnit?: DeploymentUnit): void => {
setIsNewTenantLoading(true);
setTenantLoaderText(t("extensions:manage.features.tenant.wizards.addTenant.forms.loaderMessages.tenantCreate"));
addNewTenant(tenantName)
addNewTenant(tenantName, deploymentUnit)
.then((response: AxiosResponse) => {
if (response.status === 201) {
eventPublisher.publish("create-new-organization");
Expand All @@ -162,7 +169,7 @@ export const AddTenantWizard: FunctionComponent<AddTenantWizardPropsInterface> =
delay(() => {
setIsNewTenantLoading(false);
onCloseHandler();
handleTenantSwitch(tenantName);
handleTenantSwitch(tenantName, deploymentUnit?.consoleHostname);
}, 5000);
}
})
Expand Down
Loading
Loading