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

Merged
merged 10 commits into from
Feb 28, 2025
10 changes: 10 additions & 0 deletions .changeset/long-singers-tie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@wso2is/admin.tenants.v1": minor
"@wso2is/admin.base.v1": minor
"@wso2is/admin.core.v1": minor
"@wso2is/console": minor
"@wso2is/core": minor
"@wso2is/i18n": minor
---

Central Deployment Related Changes
147 changes: 134 additions & 13 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.centralDeploymentEnabled;
var isAdaptiveAuthenticationAvailable = true;
var isOrganizationManagementEnabled = true;

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

var serverOrigin = startupConfig.serverUrl;
if (isCentralDeploymentEnabled) {
localStorage.setItem("user_requested_tenant", userTenant);
}

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 +230,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 +289,31 @@
// 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()
}

/**
* Construct the sign-in redirect URL for deployment unit.
*
* @returns {string} Constructed URL
*/
function deploymentUnitSignInRedirectURL(tenantDomain) {

var contextPath = (!proxyContextPathGlobal || proxyContextPathGlobal === "null") ? "" : "/" + proxyContextPathGlobal;

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

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

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

var endpoints = {
authorizationEndpoint: getApiPath("/oauth2/authorize"),
clockTolerance: 300,
jwksEndpointURL: isCentralDeploymentEnabled ? getApiPath("/oauth2/jwks") : undefined,
logoutEndpointURL: getApiPath("/oidc/logout"),
oidcSessionIFrameEndpointURL: getApiPath("/oidc/checksession"),
tokenEndpointURL: undefined,
tokenRevocationEndpointURL: isCentralDeploymentEnabled ? getApiPath("/oauth2/revoke") : undefined
};
if (isCentralDeploymentEnabled) {
endpoints.issuer = `${serverOrigin}/oauth2/token`;
}
var authConfig = {
signInRedirectURL: signInRedirectURL(),
signOutRedirectURL: getSignOutRedirectURL(),
Expand All @@ -344,15 +405,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 +438,81 @@
sessionStorage.setItem("auth_callback_url_console", authCallbackUrl);
}

auth.signIn(getAuthParams({}));
if (isCentralDeploymentEnabled) {
if (authorizationCode) {
auth.signIn({callOnlyOnRedirect: true})
.then((response) => {
auth.getDecodedIDToken().then((token) => {
let loginTenant = token["default_tenant"];
const associatedOrgJson = token["org_user_associations"]?.join(",");
const orgUserAssociations = associatedOrgJson ? JSON.parse(associatedOrgJson) : undefined;
const requestedTenant = localStorage.getItem("user_requested_tenant");

localStorage.removeItem("user_requested_tenant");

let loginTenantAssociation;

if (requestedTenant) {
loginTenantAssociation = orgUserAssociations?.find((org) => org.orgHandle === requestedTenant);
if (loginTenantAssociation) {
loginTenant = requestedTenant;
}
} else {
loginTenantAssociation = orgUserAssociations?.find((org) => org.orgHandle === loginTenant);
}

if (!loginTenantAssociation && orgUserAssociations?.length) {
loginTenantAssociation = orgUserAssociations[0];
}

if (!loginTenantAssociation) {
loginTenant = superTenantGlobal;
}

var regionalAuthConfig = {
signInRedirectURL: deploymentUnitSignInRedirectURL(loginTenant),
signOutRedirectURL: getSignOutRedirectURL(),
clientID: "<%= htmlWebpackPlugin.options.clientID %>",
baseUrl: getDeploymentUnitApiPath(loginTenant),
responseMode: "query",
scope: ["openid SYSTEM profile email"],
storage: "webWorker",
disableTrySignInSilently: false,
enableOIDCSessionManagement: false,
endpoints: {
authorizationEndpoint: getDeploymentUnitApiPath("/oauth2/authorize", loginTenant),
clockTolerance: 300,
jwksEndpointURL: getDeploymentUnitApiPath("/oauth2/jwks", loginTenant),
logoutEndpointURL: getDeploymentUnitApiPath("/oidc/logout", loginTenant),
oidcSessionIFrameEndpointURL: getDeploymentUnitApiPath("/oidc/checksession", loginTenant),
tokenEndpointURL: getDeploymentUnitApiPath("/oauth2/token", loginTenant),
tokenRevocationEndpointURL: getDeploymentUnitApiPath("/oauth2/revoke", loginTenant),
issuer: getDeploymentUnitApiPath("/oauth2/token", loginTenant)
},
enablePKCE: true
}
var authSecondary = AsgardeoAuth.AsgardeoSPAClient.getInstance("secondary");

authSecondary.initialize(regionalAuthConfig);
if (loginTenant !== superTenantGlobal) {
authSecondary.signIn({prompt: "login",fidp: "PlatformIDP"})
} else {
authSecondary.signIn(getAuthParams({}));
}
})
})
} 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 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()?.centralDeploymentEnabled,
clientHost: window[ "AppUtils" ]?.getConfig()?.clientOriginWithTenant,
clientID: window[ "AppUtils" ]?.getConfig()?.clientID,
clientOrigin: window[ "AppUtils" ]?.getConfig()?.clientOrigin,
Expand Down
4 changes: 4 additions & 0 deletions features/admin.core.v1/models/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,10 @@ export interface DeploymentConfigInterface extends CommonDeploymentConfigInterfa
* Configs of the myaccount app.
*/
accountApp: ExternalAppConfigInterface;
/**
* Central deployment enabled.
*/
centralDeploymentEnabled: boolean;
/**
* Configs of the developer app.
*/
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: "",
deploymentUnits: "",
entitlementPoliciesApi: "",
entitlementPolicyCombiningAlgorithmApi: "",
entitlementPolicyPublishApi: "",
Expand Down
5 changes: 3 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, 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
66 changes: 66 additions & 0 deletions features/admin.tenants.v1/api/use-get-deployment-units.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* Copyright (c) 2025, WSO2 LLC. (https://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.
*/

import useRequest, {
RequestConfigInterface,
RequestErrorInterface,
RequestResultInterface
} from "@wso2is/admin.core.v1/hooks/use-request";
import { store } from "@wso2is/admin.core.v1/store";
import { HttpMethods } from "@wso2is/core/models";
import { DeploymentUnitResponse } from "../models";

const getDomainQueryParam = (): string => {
const tenantDomain: string = store.getState().auth.tenantDomain;

return `?domain=${ tenantDomain }`;
};

/**
* Hook to get the deployment units.
*
* @param shouldFetch - Should fetch the data.
*
* @returns SWR response object containing the data, error, isLoading, isValidating, mutate.
*/
const useGetDeploymentUnits = <Data = DeploymentUnitResponse, Error = RequestErrorInterface>
( shouldFetch: boolean = false ) : RequestResultInterface<Data, Error> => {

const requestConfig: RequestConfigInterface = {
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
method: HttpMethods.GET,
url: store.getState().config.endpoints.deploymentUnits + getDomainQueryParam()
};

const { data, error, isLoading, isValidating, mutate } = useRequest<Data, Error>(
shouldFetch ? requestConfig : null
);

return {
data,
error,
isLoading,
isValidating,
mutate
};
};

export default useGetDeploymentUnits;
Loading
Loading