Skip to content

Commit

Permalink
Merge pull request #7621 from Shenali-SJ/custom-auth-login-flow-groups
Browse files Browse the repository at this point in the history
  • Loading branch information
pavinduLakshan authored Feb 15, 2025
2 parents 46c2dfd + 602ef75 commit 4b16760
Show file tree
Hide file tree
Showing 11 changed files with 145 additions and 24 deletions.
9 changes: 9 additions & 0 deletions .changeset/strange-lions-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@wso2is/admin.authentication-flow-builder.v1": patch
"@wso2is/admin.identity-providers.v1": patch
"@wso2is/admin.applications.v1": patch
"@wso2is/admin.connections.v1": patch
"@wso2is/i18n": patch
---

Add New Login Flow Authenticator Categories related to Custom Authenticators
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2023-2024, WSO2 LLC. (https://www.wso2.com).
* Copyright (c) 2023-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
Expand Down Expand Up @@ -103,6 +103,9 @@ export interface AddAuthenticatorModalPropsInterface extends TestableComponentIn
enterprise: GenericAuthenticatorInterface[];
secondFactor: GenericAuthenticatorInterface[];
recovery: GenericAuthenticatorInterface[];
external: GenericAuthenticatorInterface[];
internal: GenericAuthenticatorInterface[];
twoFactorCustom: GenericAuthenticatorInterface[];
};
/**
* Configured authentication steps.
Expand Down Expand Up @@ -272,7 +275,16 @@ export const AddAuthenticatorModal: FunctionComponent<AddAuthenticatorModalProps
t(AuthenticatorMeta.getAuthenticatorTypeDisplayName(AuthenticatorCategories.ENTERPRISE))),
...moderateAuthenticators(unfilteredAuthenticators.recovery,
AuthenticatorCategories.RECOVERY,
t(AuthenticatorMeta.getAuthenticatorTypeDisplayName(AuthenticatorCategories.RECOVERY)))
t(AuthenticatorMeta.getAuthenticatorTypeDisplayName(AuthenticatorCategories.RECOVERY))),
...moderateAuthenticators(unfilteredAuthenticators.external,
AuthenticatorCategories.EXTERNAL,
t(AuthenticatorMeta.getAuthenticatorTypeDisplayName(AuthenticatorCategories.EXTERNAL))),
...moderateAuthenticators(unfilteredAuthenticators.internal,
AuthenticatorCategories.INTERNAL,
t(AuthenticatorMeta.getAuthenticatorTypeDisplayName(AuthenticatorCategories.INTERNAL))),
...moderateAuthenticators(unfilteredAuthenticators.twoFactorCustom,
AuthenticatorCategories.TWO_FACTOR_CUSTOM,
t(AuthenticatorMeta.getAuthenticatorTypeDisplayName(AuthenticatorCategories.TWO_FACTOR_CUSTOM)))
];

// Remove organization SSO authenticator from the list, as organization SSO authenticator
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2023-2024, WSO2 LLC. (https://www.wso2.com).
* Copyright (c) 2023-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
Expand All @@ -20,8 +20,8 @@ import Chip from "@oxygen-ui/react/Chip";
import { LocalAuthenticatorConstants } from "@wso2is/admin.connections.v1/constants/local-authenticator-constants";
import { AuthenticatorMeta } from "@wso2is/admin.connections.v1/meta/authenticator-meta";
import { ConnectionsManagementUtils } from "@wso2is/admin.connections.v1/utils/connection-utils";
import { AppState } from "@wso2is/admin.core.v1/store";
import useUIConfig from "@wso2is/admin.core.v1/hooks/use-ui-configs";
import { AppState } from "@wso2is/admin.core.v1/store";
import { applicationConfig } from "@wso2is/admin.extensions.v1";
import { FeatureStatusLabel } from "@wso2is/admin.feature-gate.v1/models/feature-status";
import {
Expand Down Expand Up @@ -131,7 +131,10 @@ export const Authenticators: FunctionComponent<AuthenticatorsPropsInterface> = (
}, [ selected ]);

const isFactorEnabled = (authenticator: GenericAuthenticatorInterface): boolean => {
if (authenticator.category === AuthenticatorCategories.SECOND_FACTOR) {
if (
authenticator.category === AuthenticatorCategories.SECOND_FACTOR ||
authenticator.category === AuthenticatorCategories.TWO_FACTOR_CUSTOM
) {
// If there is only one step in the flow, second factor authenticators shouldn't be allowed.
if (currentStep === 0) {
return false;
Expand Down Expand Up @@ -195,7 +198,10 @@ export const Authenticators: FunctionComponent<AuthenticatorsPropsInterface> = (
</Label>
);

if (authenticator.category === AuthenticatorCategories.SECOND_FACTOR) {
if (
authenticator.category === AuthenticatorCategories.SECOND_FACTOR ||
authenticator.category === AuthenticatorCategories.TWO_FACTOR_CUSTOM
) {
return (
<>
{ currentStep === 0 ? (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020-2024, WSO2 LLC. (https://www.wso2.com).
* Copyright (c) 2020-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
Expand All @@ -17,6 +17,10 @@
*/

import { useRequiredScopes } from "@wso2is/access-control";
import {
isCustomAuthenticator,
isSecondFactorAuthenticator
} from "@wso2is/admin.authentication-flow-builder.v1/utils/authentication-flow-builder-utils";
import {
FederatedAuthenticatorConstants
} from "@wso2is/admin.connections.v1/constants/federated-authenticator-constants";
Expand Down Expand Up @@ -130,6 +134,9 @@ export const StepBasedFlow: FunctionComponent<AuthenticationFlowPropsInterface>
const [ localAuthenticators, setLocalAuthenticators ] = useState<GenericAuthenticatorInterface[]>([]);
const [ secondFactorAuthenticators, setSecondFactorAuthenticators ] = useState<GenericAuthenticatorInterface[]>([]);
const [ recoveryAuthenticators, setRecoveryAuthenticators ] = useState<GenericAuthenticatorInterface[]>([]);
const [ externalUserAuthenticators, setExternalUserAuthenticators ] = useState<GenericAuthenticatorInterface[]>([]);
const [ internalUserAuthenticators, setInternalUserAuthenticators ] = useState<GenericAuthenticatorInterface[]>([]);
const [ twoFactorAuthenticators, setTwoFactorAuthenticators ] = useState<GenericAuthenticatorInterface[]>([]);
const [ authenticationSteps, setAuthenticationSteps ] = useState<AuthenticationStepInterface[]>([]);
const [ subjectStepId, setSubjectStepId ] = useState<number>(1);
const [ attributeStepId, setAttributeStepId ] = useState<number>(1);
Expand Down Expand Up @@ -164,6 +171,11 @@ export const StepBasedFlow: FunctionComponent<AuthenticationFlowPropsInterface>
const filteredSocialAuthenticators: GenericAuthenticatorInterface[] = [];
const filteredEnterpriseAuthenticators: GenericAuthenticatorInterface[] = [];

// custom authenticators
const customExternalUserAuthentication: GenericAuthenticatorInterface[] = [];
const customInternalUserAuthentication: GenericAuthenticatorInterface[] = [];
const customTwoFactorAuthentication: GenericAuthenticatorInterface[] = [];

const moderatedLocalAuthenticators: GenericAuthenticatorInterface[] = [];
const secondFactorAuth: GenericAuthenticatorInterface[] = [];
const recoveryAuth: GenericAuthenticatorInterface[] = [];
Expand All @@ -173,6 +185,12 @@ export const StepBasedFlow: FunctionComponent<AuthenticationFlowPropsInterface>
recoveryAuth.push(authenticator);
} else if (ApplicationManagementConstants.SECOND_FACTOR_AUTHENTICATORS.includes(authenticator.id)) {
secondFactorAuth.push(authenticator);
} else if (SignInMethodUtils.isCustomAuthenticator(authenticator)) {
if (SignInMethodUtils.isSecondFactorAuthenticator) {
customTwoFactorAuthentication.push(authenticator);
} else {
customInternalUserAuthentication.push(authenticator);
}
} else {
moderatedLocalAuthenticators.push(authenticator);
}
Expand All @@ -186,6 +204,8 @@ export const StepBasedFlow: FunctionComponent<AuthenticationFlowPropsInterface>
} else if (ApplicationManagementConstants.SECOND_FACTOR_AUTHENTICATORS
.includes(authenticator.defaultAuthenticator.authenticatorId)) {
secondFactorAuth.push(authenticator);
} else if (SignInMethodUtils.isCustomAuthenticator(authenticator)) {
customExternalUserAuthentication.push(authenticator);
} else {
filteredEnterpriseAuthenticators.push(authenticator);
}
Expand All @@ -196,6 +216,9 @@ export const StepBasedFlow: FunctionComponent<AuthenticationFlowPropsInterface>
setLocalAuthenticators(moderatedLocalAuthenticators);
setEnterpriseAuthenticators(filteredEnterpriseAuthenticators);
setSocialAuthenticators(filteredSocialAuthenticators);
setExternalUserAuthenticators(customExternalUserAuthentication);
setInternalUserAuthenticators(customInternalUserAuthentication);
setTwoFactorAuthenticators(customTwoFactorAuthentication);
}, [ authenticators ]);

/**
Expand Down Expand Up @@ -331,7 +354,10 @@ export const StepBasedFlow: FunctionComponent<AuthenticationFlowPropsInterface>
...enterpriseAuthenticators,
...socialAuthenticators,
...secondFactorAuthenticators,
...recoveryAuthenticators
...recoveryAuthenticators,
...externalUserAuthenticators,
...internalUserAuthenticators,
...twoFactorAuthenticators
];

const authenticator: GenericAuthenticatorInterface = authenticators
Expand All @@ -356,7 +382,8 @@ export const StepBasedFlow: FunctionComponent<AuthenticationFlowPropsInterface>
const isFirstFactorAuth: boolean =
ApplicationManagementConstants.FIRST_FACTOR_AUTHENTICATORS.includes(authenticatorId);
const isSecondFactorAuth: boolean =
ApplicationManagementConstants.SECOND_FACTOR_AUTHENTICATORS.includes(authenticatorId);
ApplicationManagementConstants.SECOND_FACTOR_AUTHENTICATORS.includes(authenticatorId) ||
(isCustomAuthenticator(authenticator) && isSecondFactorAuthenticator(authenticator)) ;
const isValidSecondFactorAddition: boolean = SignInMethodUtils.isSecondFactorAdditionValid(
authenticator?.defaultAuthenticator?.authenticatorId,
stepIndex,
Expand Down Expand Up @@ -882,10 +909,13 @@ export const StepBasedFlow: FunctionComponent<AuthenticationFlowPropsInterface>
}
authenticators={ {
enterprise: enterpriseAuthenticators,
external: externalUserAuthenticators,
internal: internalUserAuthenticators,
local: localAuthenticators,
recovery: recoveryAuthenticators,
secondFactor: secondFactorAuthenticators,
social: socialAuthenticators
social: socialAuthenticators,
twoFactorCustom: twoFactorAuthenticators
} }
showStepSelector={ false }
stepCount={ authenticationSteps.length }
Expand Down Expand Up @@ -971,7 +1001,10 @@ export const StepBasedFlow: FunctionComponent<AuthenticationFlowPropsInterface>
...enterpriseAuthenticators,
...socialAuthenticators,
...secondFactorAuthenticators,
...recoveryAuthenticators
...recoveryAuthenticators,
...externalUserAuthenticators,
...internalUserAuthenticators,
...twoFactorAuthenticators
] }
onStepDelete={ handleStepDelete }
onStepOptionAuthenticatorChange={
Expand Down
10 changes: 9 additions & 1 deletion features/admin.applications.v1/utils/sign-in-method-utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2023-2024, WSO2 LLC. (https://www.wso2.com).
* Copyright (c) 2023-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
Expand Down Expand Up @@ -450,6 +450,14 @@ export class SignInMethodUtils {
return false;
}
}

public static isCustomAuthenticator = (authenticator: GenericAuthenticatorInterface): boolean => {
return authenticator?.defaultAuthenticator?.tags?.includes("Custom");
};

public static isSecondFactorAuthenticator = (authenticator: GenericAuthenticatorInterface): boolean => {
return authenticator?.defaultAuthenticator?.tags?.includes("2FA");
};
}

export type ConnectionsJITUPConflictWithMFAArgs = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com).
* Copyright (c) 2023-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
Expand Down Expand Up @@ -51,6 +51,9 @@ export type AuthenticationFlowContextProps = {
enterprise: GenericAuthenticatorInterface[];
secondFactor: GenericAuthenticatorInterface[];
recovery: GenericAuthenticatorInterface[];
external: GenericAuthenticatorInterface[];
internal: GenericAuthenticatorInterface[];
twoFactorCustom: GenericAuthenticatorInterface[];
};
/**
* List of hidden authenticators.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,18 @@ const AuthenticationFlowProvider = (props: PropsWithChildren<AuthenticationFlowP
recovery: GenericAuthenticatorInterface[];
secondFactor: GenericAuthenticatorInterface[];
social: GenericAuthenticatorInterface[];
external: GenericAuthenticatorInterface[];
internal: GenericAuthenticatorInterface[];
twoFactorCustom: GenericAuthenticatorInterface[];
}>({
enterprise: [],
external: [],
internal: [],
local: [],
recovery: [],
secondFactor: [],
social: []
social: [],
twoFactorCustom: []
});
const [ visualEditorFlowNodeMeta, setVisualEditorFlowNodeMeta ] = useState<VisualEditorFlowNodeMetaInterface>({
height: 0,
Expand Down Expand Up @@ -190,14 +196,24 @@ const AuthenticationFlowProvider = (props: PropsWithChildren<AuthenticationFlowP
const secondFactorAuthenticators: GenericAuthenticatorInterface[] = [];
const recoveryAuthenticators: GenericAuthenticatorInterface[] = [];

// custom authenticators
const customExternalUserAuthentication: GenericAuthenticatorInterface[] = [];
const customInternalUserAuthentication: GenericAuthenticatorInterface[] = [];
const customTwoFactorAuthentication: GenericAuthenticatorInterface[] = [];

const moderatedLocalAuthenticators: GenericAuthenticatorInterface[] = [];

localAuthenticators.forEach((authenticator: GenericAuthenticatorInterface) => {
if (authenticator.name === LocalAuthenticatorConstants.AUTHENTICATOR_NAMES.BACKUP_CODE_AUTHENTICATOR_NAME) {
recoveryAuthenticators.push(authenticator);
} else if (ApplicationManagementConstants.SECOND_FACTOR_AUTHENTICATORS.includes(authenticator.id) ||
isCustomLocalSecondFactorAuthenticator(authenticator)) {
} else if (ApplicationManagementConstants.SECOND_FACTOR_AUTHENTICATORS.includes(authenticator.id)) {
secondFactorAuthenticators.push(authenticator);
} else if (isCustomAuthenticator(authenticator)) {
if (isSecondFactorAuthenticator(authenticator)) {
customTwoFactorAuthentication.push(authenticator);
} else {
customInternalUserAuthentication.push(authenticator);
}
} else {
moderatedLocalAuthenticators.push(authenticator);
}
Expand All @@ -213,6 +229,12 @@ const AuthenticationFlowProvider = (props: PropsWithChildren<AuthenticationFlowP
: ConnectionsManagementUtils
.resolveConnectionResourcePath(connectionResourcesUrl, authenticator.image);

if (isCustomAuthenticator(authenticator)) {
customExternalUserAuthentication.push(authenticator);

return;
}

// Restrict the second factor authenticators being added in the first step.
if (ApplicationManagementConstants.SECOND_FACTOR_AUTHENTICATORS?.includes(
authenticator?.defaultAuthenticator?.authenticatorId)
Expand All @@ -235,10 +257,13 @@ const AuthenticationFlowProvider = (props: PropsWithChildren<AuthenticationFlowP

setFilteredAuthenticators({
enterprise: enterpriseAuthenticators,
external: customExternalUserAuthentication,
internal: customInternalUserAuthentication,
local: moderatedLocalAuthenticators,
recovery: recoveryAuthenticators,
secondFactor: secondFactorAuthenticators,
social: socialAuthenticators
social: socialAuthenticators,
twoFactorCustom: customTwoFactorAuthentication
});
}, [ authenticators ]);

Expand Down Expand Up @@ -378,7 +403,8 @@ const AuthenticationFlowProvider = (props: PropsWithChildren<AuthenticationFlowP
const isFirstFactorAuth: boolean =
ApplicationManagementConstants.FIRST_FACTOR_AUTHENTICATORS.includes(authenticatorId);
const isSecondFactorAuth: boolean =
ApplicationManagementConstants.SECOND_FACTOR_AUTHENTICATORS.includes(authenticatorId);
ApplicationManagementConstants.SECOND_FACTOR_AUTHENTICATORS.includes(authenticatorId) ||
isCustomLocalSecondFactorAuthenticator(authenticator);
const isValidSecondFactorAddition: boolean = SignInMethodUtils.isSecondFactorAdditionValid(
authenticator.defaultAuthenticator.authenticatorId,
stepIndex,
Expand Down
13 changes: 11 additions & 2 deletions features/admin.connections.v1/meta/authenticator-meta.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2023-2024, WSO2 LLC. (https://www.wso2.com).
* Copyright (c) 2023-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
Expand Down Expand Up @@ -218,7 +218,16 @@ export class AuthenticatorMeta {
"addAuthenticatorModal.content.authenticatorGroups.social.heading",
[ AuthenticatorCategories.RECOVERY ]: "applications:edit.sections.signOnMethod.sections."+
"authenticationFlow.sections.stepBased." +
"addAuthenticatorModal.content.authenticatorGroups.backupCodes.heading"
"addAuthenticatorModal.content.authenticatorGroups.backupCodes.heading",
[ AuthenticatorCategories.EXTERNAL ]: "applications:edit.sections.signOnMethod.sections."+
"authenticationFlow.sections.stepBased." +
"addAuthenticatorModal.content.authenticatorGroups.external.heading",
[ AuthenticatorCategories.INTERNAL ]: "applications:edit.sections.signOnMethod.sections."+
"authenticationFlow.sections.stepBased." +
"addAuthenticatorModal.content.authenticatorGroups.internal.heading",
[ AuthenticatorCategories.TWO_FACTOR_CUSTOM ]: "applications:edit.sections.signOnMethod.sections."+
"authenticationFlow.sections.stepBased." +
"addAuthenticatorModal.content.authenticatorGroups.twoFactorCustom.heading"
}, type);
}

Expand Down
7 changes: 5 additions & 2 deletions features/admin.connections.v1/models/authenticators.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com).
* Copyright (c) 2023-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
Expand Down Expand Up @@ -160,7 +160,10 @@ export enum AuthenticatorCategories {
LOCAL = "LOCAL",
SECOND_FACTOR = "SECOND_FACTOR",
SOCIAL = "SOCIAL",
RECOVERY = "RECOVERY"
RECOVERY = "RECOVERY",
EXTERNAL = "EXTERNAL",
INTERNAL = "INTERNAL",
TWO_FACTOR_CUSTOM = "TWO_FACTOR_CUSTOM"
}

export enum AuthenticatorSettingsFormModes {
Expand Down
Loading

0 comments on commit 4b16760

Please sign in to comment.