diff --git a/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/main/java/org/wso2/carbon/identity/central/log/mgt/utils/LogConstants.java b/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/main/java/org/wso2/carbon/identity/central/log/mgt/utils/LogConstants.java
index 5fa200d58289..d20006f00a31 100644
--- a/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/main/java/org/wso2/carbon/identity/central/log/mgt/utils/LogConstants.java
+++ b/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/main/java/org/wso2/carbon/identity/central/log/mgt/utils/LogConstants.java
@@ -161,4 +161,16 @@ public static class UserManagement {
public static final String DELETED_PERMISSIONS_FIELD = "DeletedPermissions";
public static final String DELETED_IDP_GROUPS_FIELD = "DeletedIdpGroups";
}
+
+ /**
+ * Registration flow management related log constants.
+ */
+ public static class RegistrationFlowManagement {
+
+ public static final String UPDATE_REGISTRATION_FLOW = "update-registration-flow";
+
+ private RegistrationFlowManagement() {
+
+ }
+ }
}
diff --git a/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/main/java/org/wso2/carbon/identity/central/log/mgt/utils/LoggerUtils.java b/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/main/java/org/wso2/carbon/identity/central/log/mgt/utils/LoggerUtils.java
index fb468e28c256..ab8d99d86958 100644
--- a/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/main/java/org/wso2/carbon/identity/central/log/mgt/utils/LoggerUtils.java
+++ b/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/main/java/org/wso2/carbon/identity/central/log/mgt/utils/LoggerUtils.java
@@ -80,7 +80,7 @@ public enum Initiator {
* Defines the Targets of the logs.
*/
public enum Target {
- User, Role, Group, Application, Action
+ User, Role, Group, Application, Action, Flow
}
/**
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/pom.xml b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/pom.xml
new file mode 100644
index 000000000000..ab2fdf54a4dd
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/pom.xml
@@ -0,0 +1,165 @@
+
+
+
+
+
+ org.wso2.carbon.identity.framework
+ identity-framework
+ 7.8.28-SNAPSHOT
+ ../../../pom.xml
+
+
+ 4.0.0
+ org.wso2.carbon.identity.user.registration.mgt
+ bundle
+ WSO2 Carbon - Identity User Self Registration Flow Manager
+ WSO2 user self registration flow management
+ http://www.wso2.com
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ provided
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ provided
+
+
+ org.testng
+ testng
+ test
+
+
+ org.ops4j.pax.logging
+ pax-logging-api
+
+
+ org.wso2.carbon.utils
+ org.wso2.carbon.database.utils
+
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.core
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.slf4j
+ jcl-over-slf4j
+
+
+ org.slf4j
+ log4j-over-slf4j
+
+
+
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.central.log.mgt
+
+
+ org.wso2.carbon
+ org.wso2.carbon.utils
+
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.testutil
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ com.h2database
+ h2
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 8
+ 8
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+
+ true
+
+
+
+ org.codehaus.mojo
+ findbugs-maven-plugin
+
+ true
+
+
+
+ org.apache.felix
+ maven-bundle-plugin
+ true
+
+
+ ${project.artifactId}
+ ${project.artifactId}
+
+ org.wso2.carbon.identity.user.registration.mgt.internal
+
+
+ javax.xml.parsers; version="${javax.xml.parsers.import.pkg.version}",
+ org.xml.sax,
+ org.w3c.dom,
+ org.osgi.framework; version="${osgi.framework.imp.pkg.version.range}",
+ org.apache.xerces.util.*; resolution:=optional,
+ org.apache.commons.logging; version="${import.package.version.commons.logging}",
+ org.osgi.service.component; version="${osgi.service.component.imp.pkg.version.range}",
+ org.wso2.carbon.utils.*; version="${carbon.kernel.package.import.version.range}",
+ org.wso2.carbon.database.utils.jdbc;version="${org.wso2.carbon.database.utils.version.range}",
+ org.wso2.carbon.database.utils.jdbc.exceptions;version="${org.wso2.carbon.database.utils.version.range}",
+ org.wso2.carbon.identity.core.util;
+ version="${carbon.identity.package.import.version.range}",
+ org.wso2.carbon.identity.central.log.mgt.utils;
+ version="${carbon.identity.package.import.version.range}",
+ javax.servlet.http; version="${imp.pkg.version.javax.servlet}"
+
+
+ !org.wso2.carbon.identity.user.registration.mgt.internal,
+ org.wso2.carbon.identity.user.registration.mgt.*;
+ version="${carbon.identity.package.export.version}"
+
+
+
+
+
+
+
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/Constants.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/Constants.java
new file mode 100644
index 000000000000..4ff88efda771
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/Constants.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * 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.carbon.identity.user.registration.mgt;
+
+/**
+ * Constants for the self registration flow.
+ */
+public class Constants {
+
+ public static final String COMPLETE = "COMPLETE";
+ public static final String DEFAULT_FLOW_NAME = "defaultFlow";
+
+ private Constants() {
+
+ }
+
+ public enum ErrorMessages {
+
+ // Server errors.
+ ERROR_CODE_ADD_DEFAULT_FLOW("65001", "Error while updating the flow.",
+ "Unexpected server error while updating the default flow in tenant, %s"),
+
+ ERROR_CODE_GET_DEFAULT_FLOW("65002", "Error while retrieving the flow.",
+ "Unexpected server error while retrieving the default flow from database in " +
+ "tenant, %s"),
+
+ ERROR_CODE_SERIALIZE_PAGE_CONTENT("65003", "Error while serializing the page content.",
+ "Unexpected server error while serializing the page content for step %s" +
+ " in tenant, %s"),
+ ERROR_CODE_INVALID_NODE("65004", "Node id not found.", "Could not resolve a valid node with id, %s to create " +
+ "a link."),
+ ERROR_CODE_DESERIALIZE_PAGE_CONTENT("65005", "Error while deserializing the page content.",
+ "Unexpected server error while deserializing the page content for step %s" +
+ " in tenant, %s"),
+ ERROR_CODE_GET_REG_GRAPH_FAILED("65006", "Error while retrieving the registration graph.",
+ "Unexpected server error while retrieving the registration graph for tenant, " +
+ "%s"),
+
+ // Client errors.
+ ERROR_CODE_UNSUPPORTED_STEP_TYPE("60001", "Unsupported step type.",
+ "The step type, %s is not supported."),
+ ERROR_CODE_STEP_DATA_NOT_FOUND("60002", "Step data not found.",
+ "The step data for step, %s is not found."),
+ ERROR_CODE_COMPONENT_DATA_NOT_FOUND("60003", "Component data not found.",
+ "The step, %s of type view must have components defined."),
+ ERROR_CODE_INVALID_ACTION_FOR_BUTTON("60004", "Invalid action configuration.",
+ "The component, %s of type button must have an action defined."),
+ ERROR_CODE_NEXT_ACTION_NOT_FOUND("60005", "Invalid action configuration.",
+ "Next step is not defined for the action in component, %s."),
+ ERROR_CODE_EXECUTOR_INFO_NOT_FOUND("60006", "Invalid action configuration.",
+ "Executor data is not defined for the action type EXECUTOR in component, " +
+ "%s."),
+ ERROR_CODE_MULTIPLE_STEP_EXECUTORS("60007", "Multiple executors defined for the step.",
+ "Multiple executors are defined for the step, %s."),
+ ERROR_CODE_UNSUPPORTED_ACTION_TYPE("60008", "Unsupported action type.",
+ "The action type, %s defined for component %s is not supported."),
+ ERROR_CODE_INVALID_NEXT_STEP("600009", "Invalid next step configuration.", "Cannot resolve a step for the " +
+ "next id, %s."),
+ ERROR_CODE_ACTION_DATA_NOT_FOUND("60010", "Action data not found.",
+ "The step, %s of type redirection must have action defined"),
+ ERROR_CODE_INVALID_ACTION_TYPE("60011", "Invalid action type.",
+ "The action type, %s is not supported for step, %s of type redirection."),
+ ;
+
+ private static final String ERROR_PREFIX = "RFM";
+ private final String code;
+ private final String message;
+ private final String description;
+
+ ErrorMessages(String code, String message, String description) {
+
+ this.code = ERROR_PREFIX + "-" + code;
+ this.message = message;
+ this.description = description;
+ }
+
+ public String getCode() {
+
+ return code;
+ }
+
+ public String getMessage() {
+
+ return message;
+ }
+
+ public String getDescription() {
+
+ return description;
+ }
+
+ @Override
+ public String toString() {
+
+ return code + ":" + message;
+ }
+ }
+
+ /**
+ * Constants for the node types.
+ */
+ public static class NodeTypes {
+
+ public static final String DECISION = "DECISION";
+ public static final String TASK_EXECUTION = "TASK_EXECUTION";
+ public static final String PROMPT_ONLY = "PROMPT_ONLY";
+
+ private NodeTypes() {
+
+ }
+ }
+
+ /**
+ * Constants for the step types.
+ */
+ public static class StepTypes {
+
+ public static final String VIEW = "VIEW";
+ public static final String REDIRECTION = "REDIRECTION";
+
+ private StepTypes() {
+
+ }
+ }
+
+ public static class ComponentTypes {
+
+ public static final String FORM = "FORM";
+ public static final String BUTTON = "BUTTON";
+
+ private ComponentTypes() {
+
+ }
+ }
+
+ /**
+ * Constants for the action types.
+ */
+ public static class ActionTypes {
+
+ public static final String EXECUTOR = "EXECUTOR";
+ public static final String NEXT = "NEXT";
+
+ private ActionTypes() {
+
+ }
+ }
+
+ /**
+ * Constants for defined executor types.
+ */
+ public static class ExecutorTypes {
+
+ public static final String USER_ONBOARDING = "UserOnboardingExecutor";
+
+ private ExecutorTypes() {
+
+ }
+ }
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/RegistrationFlowMgtService.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/RegistrationFlowMgtService.java
new file mode 100644
index 000000000000..8595f7bb8fbd
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/RegistrationFlowMgtService.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * 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.carbon.identity.user.registration.mgt;
+
+import static org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils.triggerAuditLogEvent;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.DEFAULT_FLOW_NAME;
+import static org.wso2.carbon.identity.user.registration.mgt.utils.RegistrationMgtUtils.getInitiatorId;
+import static org.wso2.carbon.identity.user.registration.mgt.utils.RegistrationMgtUtils.isEnableV2AuditLogs;
+import org.wso2.carbon.identity.central.log.mgt.utils.LogConstants;
+import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils;
+import org.wso2.carbon.identity.user.registration.mgt.dao.RegistrationFlowDAO;
+import org.wso2.carbon.identity.user.registration.mgt.dao.RegistrationFlowDAOImpl;
+import org.wso2.carbon.identity.user.registration.mgt.exception.RegistrationFrameworkException;
+import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationFlowDTO;
+import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationGraphConfig;
+import org.wso2.carbon.identity.user.registration.mgt.utils.GraphBuilder;
+import org.wso2.carbon.utils.AuditLog;
+
+/**
+ * This class is responsible for managing the registration flow.
+ */
+public class RegistrationFlowMgtService {
+
+ private static final RegistrationFlowMgtService instance = new RegistrationFlowMgtService();
+ private static final RegistrationFlowDAO registrationFlowDAO = new RegistrationFlowDAOImpl();
+
+ private RegistrationFlowMgtService() {
+
+ }
+
+ public static RegistrationFlowMgtService getInstance() {
+
+ return instance;
+ }
+
+ /**
+ * Update the default registration flow of the given tenant.
+ *
+ * @param flowDTO The registration flow.
+ * @param tenantID The tenant ID.
+ */
+ public void updateDefaultRegistrationFlow(RegistrationFlowDTO flowDTO, int tenantID)
+ throws RegistrationFrameworkException {
+
+ RegistrationGraphConfig flowConfig = GraphBuilder.convert(flowDTO);
+ registrationFlowDAO.updateDefaultRegistrationFlowByTenant(flowConfig, tenantID, DEFAULT_FLOW_NAME);
+ if (isEnableV2AuditLogs()) {
+ AuditLog.AuditLogBuilder auditLogBuilder =
+ new AuditLog.AuditLogBuilder(getInitiatorId(), LoggerUtils.getInitiatorType(getInitiatorId()),
+ flowConfig.getId(),
+ LoggerUtils.Target.Flow.name(),
+ LogConstants.RegistrationFlowManagement.UPDATE_REGISTRATION_FLOW);
+ triggerAuditLogEvent(auditLogBuilder, true);
+ }
+ }
+
+ /**
+ * Get the default registration flow of the given tenant.
+ *
+ * @param tenantID The tenant ID.
+ * @return The registration flow.
+ * @throws RegistrationFrameworkException If an error occurs while retrieving the default flow.
+ */
+ public RegistrationFlowDTO getRegistrationFlow(int tenantID) throws RegistrationFrameworkException {
+
+ return registrationFlowDAO.getDefaultRegistrationFlowByTenant(tenantID);
+ }
+
+ /**
+ * Get the registration flow by tenant ID.
+ *
+ * @param tenantID The tenant ID.
+ */
+ public RegistrationGraphConfig getRegistrationFlowConfig(int tenantID) {
+
+ // TODO: Implement this method.
+ return null;
+ }
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/dao/RegistrationFlowDAO.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/dao/RegistrationFlowDAO.java
new file mode 100644
index 000000000000..326cb4e98369
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/dao/RegistrationFlowDAO.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * 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.carbon.identity.user.registration.mgt.dao;
+
+import org.wso2.carbon.identity.user.registration.mgt.exception.RegistrationFrameworkException;
+import org.wso2.carbon.identity.user.registration.mgt.exception.RegistrationServerException;
+import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationFlowDTO;
+import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationGraphConfig;
+
+/**
+ * DAO interface for registration flow management.
+ */
+public interface RegistrationFlowDAO {
+
+ /**
+ * Update the default registration flow of the given tenant.
+ *
+ * @param regFlowConfig The registration flow.
+ * @param tenantId The tenant ID.
+ * @param flowName The flow name.
+ * @throws RegistrationFrameworkException If an error occurs while updating the default flow.
+ */
+ void updateDefaultRegistrationFlowByTenant(RegistrationGraphConfig regFlowConfig, int tenantId, String flowName)
+ throws RegistrationFrameworkException;
+
+ /**
+ * Get the default registration flow of the given tenant.
+ *
+ * @param tenantId The tenant ID.
+ * @return The registration flow.
+ * @throws RegistrationServerException If an error occurs while retrieving the default flow.
+ */
+ RegistrationFlowDTO getDefaultRegistrationFlowByTenant(int tenantId) throws RegistrationServerException;
+
+ /**
+ * Get the default registration graph of the given tenant.
+ *
+ * @param tenantId The tenant ID.
+ * @return The registration graph.
+ * @throws RegistrationServerException If an error occurs while retrieving the flow.
+ */
+ RegistrationGraphConfig getDefaultRegistrationGraphByTenant(int tenantId) throws RegistrationFrameworkException;
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/dao/RegistrationFlowDAOImpl.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/dao/RegistrationFlowDAOImpl.java
new file mode 100644
index 000000000000..ff1ba9bda278
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/dao/RegistrationFlowDAOImpl.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * 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.carbon.identity.user.registration.mgt.dao;
+
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.StepTypes.REDIRECTION;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.StepTypes.VIEW;
+import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.DELETE_FLOW;
+import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.GET_FLOW;
+import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.INSERT_FLOW_INTO_IDN_FLOW;
+import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.INSERT_FLOW_NODE_INFO;
+import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.INSERT_FLOW_PAGE_INFO;
+import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.INSERT_FLOW_PAGE_META;
+import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.INSERT_NODE_EDGES;
+import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.INSERT_NODE_EXECUTOR_INFO;
+import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.SQLPlaceholders.COORDINATE_X;
+import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.SQLPlaceholders.COORDINATE_Y;
+import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.SQLPlaceholders.HEIGHT;
+import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.SQLPlaceholders.PAGE_CONTENT;
+import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.SQLPlaceholders.PAGE_TYPE;
+import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.SQLPlaceholders.REGISTRATION_FLOW;
+import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.SQLPlaceholders.STEP_ID;
+import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.SQLPlaceholders.WIDTH;
+import static org.wso2.carbon.identity.user.registration.mgt.utils.RegistrationMgtUtils.handleClientException;
+import static org.wso2.carbon.identity.user.registration.mgt.utils.RegistrationMgtUtils.handleServerException;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.wso2.carbon.database.utils.jdbc.JdbcTemplate;
+import org.wso2.carbon.database.utils.jdbc.exceptions.DataAccessException;
+import org.wso2.carbon.database.utils.jdbc.exceptions.TransactionException;
+import org.wso2.carbon.identity.core.util.JdbcUtils;
+import org.wso2.carbon.identity.core.util.LambdaExceptionUtils;
+import org.wso2.carbon.identity.user.registration.mgt.Constants;
+import org.wso2.carbon.identity.user.registration.mgt.exception.RegistrationFrameworkException;
+import org.wso2.carbon.identity.user.registration.mgt.exception.RegistrationServerException;
+import org.wso2.carbon.identity.user.registration.mgt.model.ActionDTO;
+import org.wso2.carbon.identity.user.registration.mgt.model.ComponentDTO;
+import org.wso2.carbon.identity.user.registration.mgt.model.DataDTO;
+import org.wso2.carbon.identity.user.registration.mgt.model.ExecutorDTO;
+import org.wso2.carbon.identity.user.registration.mgt.model.NodeConfig;
+import org.wso2.carbon.identity.user.registration.mgt.model.NodeEdge;
+import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationFlowDTO;
+import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationGraphConfig;
+import org.wso2.carbon.identity.user.registration.mgt.model.StepDTO;
+
+/**
+ * The DAO class for the registration flow.
+ */
+public class RegistrationFlowDAOImpl implements RegistrationFlowDAO {
+
+ private static final Log LOG = LogFactory.getLog(RegistrationFlowDAOImpl.class);
+
+ private static byte[] serializeObject(Object obj) throws IOException {
+
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos)) {
+ oos.writeObject(obj);
+ oos.flush();
+ return baos.toByteArray();
+ }
+ }
+
+
+ private static byte[] serializeStepData(StepDTO stepDTO, int tenantId)
+ throws RegistrationFrameworkException {
+
+ try {
+ if (VIEW.equals(stepDTO.getType())) {
+ List components = stepDTO.getData().getComponents();
+ return serializeObject(components);
+ } else if (REDIRECTION.equals(stepDTO.getType())) {
+ ActionDTO action = stepDTO.getData().getAction();
+ return serializeObject(action);
+ } else {
+ throw handleClientException(Constants.ErrorMessages.ERROR_CODE_UNSUPPORTED_STEP_TYPE,
+ stepDTO.getType());
+ }
+ } catch (IOException e) {
+ throw handleServerException(Constants.ErrorMessages.ERROR_CODE_SERIALIZE_PAGE_CONTENT, e,
+ stepDTO.getId(), tenantId);
+ }
+ }
+
+ @Override
+ public void updateDefaultRegistrationFlowByTenant(RegistrationGraphConfig regFlowConfig, int tenantId,
+ String flowName) throws RegistrationFrameworkException {
+
+ String flowId = regFlowConfig.getId();
+ JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate();
+ try {
+ jdbcTemplate.withTransaction(template -> {
+ // Delete the existing flow for the tenant.
+ template.executeUpdate(DELETE_FLOW,
+ preparedStatement -> {
+ preparedStatement.setInt(1, tenantId);
+ preparedStatement.setString(2, REGISTRATION_FLOW);
+ });
+
+ // Insert into IDN_FLOW.
+ template.executeInsert(INSERT_FLOW_INTO_IDN_FLOW,
+ preparedStatement -> {
+ preparedStatement.setString(1, flowId);
+ preparedStatement.setInt(2, tenantId);
+ preparedStatement.setString(3, flowName);
+ preparedStatement.setString(4, REGISTRATION_FLOW);
+ preparedStatement.setBoolean(5, true);
+ }, regFlowConfig, false);
+
+ // Insert into IDN_FLOW_NODE.
+ Map nodeIdToRegNodeIdMap = new HashMap<>();
+ for (Map.Entry entry : regFlowConfig.getNodeConfigs().entrySet()) {
+ NodeConfig node = entry.getValue();
+ int regNodeId = template.executeInsert(INSERT_FLOW_NODE_INFO,
+ preparedStatement -> {
+ preparedStatement.setString(1, node.getId());
+ preparedStatement.setString(2, flowId);
+ preparedStatement.setString(3, node.getType());
+ preparedStatement.setBoolean(4, node.isFirstNode());
+ }, entry, true);
+
+ nodeIdToRegNodeIdMap.put(node.getId(), regNodeId);
+
+ // Insert into IDN_FLOW_NODE_EXECUTOR.
+ ExecutorDTO executorConfig = node.getExecutorConfig();
+ if (executorConfig != null) {
+ template.executeInsert(INSERT_NODE_EXECUTOR_INFO,
+ preparedStatement -> {
+ preparedStatement.setInt(1,
+ nodeIdToRegNodeIdMap.get(node.getId()));
+ preparedStatement.setString(2, executorConfig.getName());
+ preparedStatement.setString(3, executorConfig.getIdpName());
+ }, null, false);
+ }
+ }
+ // Insert graph edges into IDN_FLOW_NODE_MAPPING.
+ for (Map.Entry entry : regFlowConfig.getNodeConfigs().entrySet()) {
+ NodeConfig node = entry.getValue();
+ if (node.getEdges() != null) {
+ for (NodeEdge edge : node.getEdges()) {
+ template.executeInsert(INSERT_NODE_EDGES,
+ preparedStatement -> {
+ preparedStatement.setInt(1, nodeIdToRegNodeIdMap.get(
+ edge.getSourceNodeId()));
+ preparedStatement.setInt(2, nodeIdToRegNodeIdMap.get(
+ edge.getTargetNodeId()));
+ preparedStatement.setString(3, edge.getTriggeringActionId());
+ }, null, false);
+ }
+ }
+ }
+
+ // Insert into IDN_FLOW_PAGE.
+ for (Map.Entry entry : regFlowConfig.getNodePageMappings().entrySet()) {
+
+ StepDTO stepDTO = entry.getValue();
+ byte[] pageContent = serializeStepData(stepDTO, tenantId);
+ int regNodeId = nodeIdToRegNodeIdMap.get(entry.getKey());
+
+ int pageAutoIncId = template.executeInsert(INSERT_FLOW_PAGE_INFO,
+ preparedStatement -> {
+ preparedStatement.setString(1, flowId);
+ preparedStatement.setInt(2, regNodeId);
+ preparedStatement.setString(3, stepDTO.getId());
+ preparedStatement.setBinaryStream(4,
+ new ByteArrayInputStream(
+ pageContent));
+ preparedStatement.setString(5, stepDTO.getType());
+ }, entry, true);
+
+ // Insert into IDN_FLOW_PAGE_META.
+ template.executeInsert(INSERT_FLOW_PAGE_META,
+ preparedStatement -> {
+ preparedStatement.setInt(1, pageAutoIncId);
+ preparedStatement.setDouble(2, stepDTO.getCoordinateX());
+ preparedStatement.setDouble(3, stepDTO.getCoordinateY());
+ preparedStatement.setDouble(4, stepDTO.getCoordinateX());
+ preparedStatement.setDouble(5, stepDTO.getCoordinateY());
+ }, null, false);
+ }
+ return null;
+ });
+ } catch (TransactionException e) {
+ throw handleServerException(Constants.ErrorMessages.ERROR_CODE_ADD_DEFAULT_FLOW, e, tenantId);
+ }
+ }
+
+ @Override
+ public RegistrationFlowDTO getDefaultRegistrationFlowByTenant(int tenantId) throws RegistrationServerException {
+
+ JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate();
+
+ try {
+ List steps = jdbcTemplate
+ .executeQuery(GET_FLOW, (LambdaExceptionUtils.rethrowRowMapper((resultSet, rowNumber) -> {
+ StepDTO stepDTO = new StepDTO.Builder()
+ .id(resultSet.getString(STEP_ID))
+ .type(resultSet.getString(PAGE_TYPE))
+ .coordinateX(resultSet.getDouble(COORDINATE_X))
+ .coordinateY(resultSet.getDouble(COORDINATE_Y))
+ .height(resultSet.getDouble(HEIGHT))
+ .width(resultSet.getDouble(WIDTH))
+ .build();
+
+ resolvePageContent(stepDTO, resultSet.getBinaryStream(PAGE_CONTENT), tenantId);
+ return stepDTO;
+ })), preparedStatement -> preparedStatement.setInt(1, tenantId));
+
+ RegistrationFlowDTO registrationFlowDTO = new RegistrationFlowDTO();
+ if (steps.isEmpty()) {
+ LOG.debug("No steps are found in the default flow of tenant " + tenantId);
+ return registrationFlowDTO;
+ }
+ registrationFlowDTO.getSteps().addAll(steps);
+ return registrationFlowDTO;
+ } catch (DataAccessException e) {
+ throw handleServerException(Constants.ErrorMessages.ERROR_CODE_GET_DEFAULT_FLOW, e, tenantId);
+ }
+ }
+
+ private void resolvePageContent(StepDTO stepDTO, InputStream pageContent, int tenantId)
+ throws DataAccessException, RegistrationServerException {
+
+ try (ObjectInputStream ois = new ObjectInputStream(pageContent)) {
+ Object obj = ois.readObject();
+ if (VIEW.equals(stepDTO.getType()) && obj instanceof List>) {
+ List> tempList = (List>) obj;
+ if (!tempList.isEmpty() && tempList.get(0) instanceof ComponentDTO) {
+ List components = tempList.stream()
+ .map(ComponentDTO.class::cast)
+ .collect(Collectors.toList());
+ stepDTO.setData(new DataDTO.Builder().components(components).build());
+ } else {
+ throw handleServerException(Constants.ErrorMessages.ERROR_CODE_DESERIALIZE_PAGE_CONTENT,
+ stepDTO.getId(), tenantId);
+ }
+ } else if (REDIRECTION.equals(stepDTO.getType())) {
+ if (obj instanceof ActionDTO) {
+ ActionDTO action = (ActionDTO) obj;
+ stepDTO.setData(new DataDTO.Builder().action(action).build());
+ }
+ }
+ } catch (IOException | ClassNotFoundException e) {
+ throw handleServerException(Constants.ErrorMessages.ERROR_CODE_DESERIALIZE_PAGE_CONTENT, e, stepDTO.getId(),
+ tenantId);
+ }
+ }
+
+ @Override
+ public RegistrationGraphConfig getDefaultRegistrationGraphByTenant(int tenantId)
+ throws RegistrationFrameworkException {
+
+ return null;
+ }
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/dao/SQLConstants.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/dao/SQLConstants.java
new file mode 100644
index 000000000000..f38fd038c772
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/dao/SQLConstants.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * 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.carbon.identity.user.registration.mgt.dao;
+
+/**
+ * This class holds the database queries and constants for the flow management DAO layer.
+ */
+public class SQLConstants {
+
+ public static final String DELETE_FLOW =
+ "DELETE FROM IDN_FLOW WHERE TENANT_ID = ? AND IS_DEFAULT = TRUE AND TYPE = ?";
+ public static final String INSERT_FLOW_INTO_IDN_FLOW =
+ "INSERT INTO IDN_FLOW (ID, TENANT_ID, FLOW_NAME, TYPE, IS_DEFAULT) VALUES (?, ?, ?, ?, ?)";
+ public static final String INSERT_FLOW_NODE_INFO =
+ "INSERT INTO IDN_FLOW_NODE (NODE_ID, FLOW_ID, NODE_TYPE, IS_FIRST_NODE) VALUES (?, ?, ?, ?)";
+ public static final String INSERT_NODE_EXECUTOR_INFO =
+ "INSERT INTO IDN_FLOW_NODE_EXECUTOR (FLOW_NODE_ID, EXECUTOR_NAME, IDP_NAME) VALUES (?, ?, ?)";
+ public static final String INSERT_NODE_EDGES =
+ "INSERT INTO IDN_FLOW_NODE_MAPPING (FLOW_NODE_ID, NEXT_NODE_ID, TRIGGERING_ELEMENT) VALUES (?, ?, ?)";
+ public static final String INSERT_FLOW_PAGE_INFO =
+ "INSERT INTO IDN_FLOW_PAGE (FLOW_ID, FLOW_NODE_ID, STEP_ID, PAGE_CONTENT, TYPE) VALUES (?, ?, ?, ?, ?)";
+ public static final String INSERT_FLOW_PAGE_META =
+ "INSERT INTO IDN_FLOW_PAGE_META (PAGE_ID, COORDINATE_X, COORDINATE_Y, HEIGHT, WIDTH) VALUES (?, ?, ?, ?, " +
+ "?)";
+ public static final String GET_FLOW =
+ "SELECT P.ID AS PAGE_ID, P.STEP_ID, P.PAGE_CONTENT, P.TYPE AS PAGE_TYPE, M.COORDINATE_X, M" +
+ ".COORDINATE_Y, M.HEIGHT, M.WIDTH FROM IDN_FLOW F JOIN IDN_FLOW_PAGE P ON F.ID = P.FLOW_ID " +
+ "LEFT JOIN IDN_FLOW_PAGE_META M ON P.ID = M.PAGE_ID WHERE F.TENANT_ID = ? AND F.IS_DEFAULT = " +
+ "TRUE AND F.TYPE = 'REGISTRATION';";
+
+ private SQLConstants() {
+
+ }
+
+ /**
+ * SQL Placeholders.
+ */
+ public static final class SQLPlaceholders {
+
+ public static final String REGISTRATION_FLOW = "REGISTRATION";
+ public static final String STEP_ID = "STEP_ID";
+ public static final String PAGE_CONTENT = "PAGE_CONTENT";
+ public static final String PAGE_TYPE = "PAGE_TYPE";
+ public static final String COORDINATE_X = "COORDINATE_X";
+ public static final String COORDINATE_Y = "COORDINATE_Y";
+ public static final String HEIGHT = "HEIGHT";
+ public static final String WIDTH = "WIDTH";
+
+ private SQLPlaceholders() {
+
+ }
+ }
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/exception/RegistrationClientException.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/exception/RegistrationClientException.java
new file mode 100644
index 000000000000..eee93d9be70c
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/exception/RegistrationClientException.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * 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.carbon.identity.user.registration.mgt.exception;
+
+/**
+ * Represents the client exception that occurs in the registration framework.
+ */
+public class RegistrationClientException extends RegistrationFrameworkException {
+
+ public RegistrationClientException(String message) {
+
+ super(message);
+ }
+
+ public RegistrationClientException(String errorCode, String message, String description, Throwable cause) {
+
+ super(errorCode, message, description, cause);
+ }
+
+ public RegistrationClientException(String errorCode, String message, String description) {
+
+ super(errorCode, message, description);
+ }
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/exception/RegistrationFrameworkException.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/exception/RegistrationFrameworkException.java
new file mode 100644
index 000000000000..26006c345c6d
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/exception/RegistrationFrameworkException.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * 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.carbon.identity.user.registration.mgt.exception;
+
+/**
+ * Represents the exception that occurs in the registration framework.
+ */
+public class RegistrationFrameworkException extends Exception {
+
+ private String errorCode;
+ private String description;
+
+ public RegistrationFrameworkException(String message) {
+
+ super(message);
+ }
+
+ public RegistrationFrameworkException(String errorCode, String message, String description, Throwable cause) {
+
+ super(message, cause);
+ this.errorCode = errorCode;
+ this.description = description;
+ }
+
+ public RegistrationFrameworkException(String errorCode, String message, String description) {
+
+ super(message);
+ this.errorCode = errorCode;
+ this.description = description;
+ }
+
+ public String getErrorCode() {
+
+ return errorCode;
+ }
+
+ public void setErrorCode(String errorCode) {
+
+ this.errorCode = errorCode;
+ }
+
+ public String getDescription() {
+
+ return description;
+ }
+
+ public void setDescription(String description) {
+
+ this.description = description;
+ }
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/exception/RegistrationServerException.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/exception/RegistrationServerException.java
new file mode 100644
index 000000000000..ada15a4b7739
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/exception/RegistrationServerException.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * 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.carbon.identity.user.registration.mgt.exception;
+
+/**
+ * Represents the server exception that occurs in the registration framework.
+ */
+public class RegistrationServerException extends RegistrationFrameworkException {
+
+ public RegistrationServerException(String message) {
+
+ super(message);
+ }
+
+ public RegistrationServerException(String errorCode, String message, String description, Throwable cause) {
+
+ super(errorCode, message, description, cause);
+ }
+
+ public RegistrationServerException(String errorCode, String message, String description) {
+
+ super(errorCode, message, description);
+ }
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/internal/RegistrationFlowMgtServiceComponent.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/internal/RegistrationFlowMgtServiceComponent.java
new file mode 100644
index 000000000000..55b9a62ff2b2
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/internal/RegistrationFlowMgtServiceComponent.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * 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.carbon.identity.user.registration.mgt.internal;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.wso2.carbon.identity.user.registration.mgt.RegistrationFlowMgtService;
+
+@Component(
+ name = "user.registration.flow.mgt.component",
+ immediate = true)
+public class RegistrationFlowMgtServiceComponent {
+
+ private static final Log LOG = LogFactory.getLog(RegistrationFlowMgtServiceComponent.class);
+
+ @Activate
+ protected void activate(ComponentContext context) {
+
+ BundleContext bundleContext = context.getBundleContext();
+ bundleContext.registerService(RegistrationFlowMgtService.class.getName(),
+ RegistrationFlowMgtService.getInstance(), null);
+ LOG.debug("RegistrationFlowMgt bundle is activated.");
+ }
+
+ @Deactivate
+ protected void deactivate(ComponentContext context) {
+
+ BundleContext bundleCtx = context.getBundleContext();
+ bundleCtx.ungetService(bundleCtx.getServiceReference(RegistrationFlowMgtService.class));
+ LOG.debug("RegistrationFlowMgt bundle is deactivated.");
+ }
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ActionDTO.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ActionDTO.java
new file mode 100644
index 000000000000..5ae3aa03e581
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ActionDTO.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * 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.carbon.identity.user.registration.mgt.model;
+
+import java.io.Serializable;
+
+/**
+ * DTO class for Action.
+ */
+public class ActionDTO implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+ private String type;
+ private ExecutorDTO executor;
+ private String nextId;
+
+ public ActionDTO() {
+
+ }
+
+ private ActionDTO(Builder builder) {
+
+ this.type = builder.type;
+ this.executor = builder.executor;
+ this.nextId = builder.nextId;
+ }
+
+ public String getType() {
+
+ return type;
+ }
+
+ public void setType(String type) {
+
+ this.type = type;
+ }
+
+ public ExecutorDTO getExecutor() {
+
+ return executor;
+ }
+
+ public void setExecutor(ExecutorDTO executor) {
+
+ this.executor = executor;
+ }
+
+ public String getNextId() {
+
+ return nextId;
+ }
+
+ public void setNextId(String nextId) {
+
+ this.nextId = nextId;
+ }
+
+ /**
+ * Builder class to build {@link ActionDTO} objects.
+ */
+ public static class Builder {
+
+ private String type;
+ private ExecutorDTO executor;
+ private String nextId;
+
+ public Builder type(String type) {
+
+ this.type = type;
+ return this;
+ }
+
+ public Builder executor(ExecutorDTO executor) {
+
+ this.executor = executor;
+ return this;
+ }
+
+ public Builder nextId(String nextId) {
+
+ this.nextId = nextId;
+ return this;
+ }
+
+ public ActionDTO build() {
+
+ return new ActionDTO(this);
+ }
+ }
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ComponentDTO.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ComponentDTO.java
new file mode 100644
index 000000000000..ba1567366766
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ComponentDTO.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * 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.carbon.identity.user.registration.mgt.model;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * DTO class for component in the step.
+ */
+public class ComponentDTO implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+ private String id;
+ private String category;
+ private String type;
+ private String identifier;
+ private ActionDTO action;
+ private Map configs = new HashMap<>();
+ private List components = new ArrayList<>();
+
+ public ComponentDTO() {
+
+ }
+
+ private ComponentDTO(Builder builder) {
+
+ this.id = builder.id;
+ this.category = builder.category;
+ this.type = builder.type;
+ this.identifier = builder.identifier;
+ this.configs = builder.configs;
+ this.components = builder.components;
+ this.action = builder.action;
+ }
+
+ public String getId() {
+
+ return id;
+ }
+
+ public void setId(String id) {
+
+ this.id = id;
+ }
+
+ public String getType() {
+
+ return type;
+ }
+
+ public void setType(String type) {
+
+ this.type = type;
+ }
+
+ public Map getConfigs() {
+
+ return configs;
+ }
+
+ public void addConfig(String key, Object value) {
+
+ this.configs.put(key, value);
+ }
+
+ public ActionDTO getAction() {
+
+ return action;
+ }
+
+ public void setAction(ActionDTO action) {
+
+ this.action = action;
+ }
+
+ public String getIdentifier() {
+
+ return identifier;
+ }
+
+ public void setIdentifier(String identifier) {
+
+ this.identifier = identifier;
+ }
+
+ public String getCategory() {
+
+ return category;
+ }
+
+ public void setCategory(String category) {
+
+ this.category = category;
+ }
+
+ public List getComponents() {
+
+ return components;
+ }
+
+ public void setComponents(List components) {
+
+ this.components = components;
+ }
+
+ /**
+ * Builder class to build {@link ComponentDTO} objects.
+ */
+ public static class Builder {
+
+ private Map configs = new HashMap<>();
+ private List components = new ArrayList<>();
+ private String id;
+ private String category;
+ private String type;
+ private String identifier;
+ private ActionDTO action;
+
+ public Builder id(String id) {
+
+ this.id = id;
+ return this;
+ }
+
+ public Builder category(String category) {
+
+ this.category = category;
+ return this;
+ }
+
+ public Builder type(String type) {
+
+ this.type = type;
+ return this;
+ }
+
+ public Builder identifier(String identifier) {
+
+ this.identifier = identifier;
+ return this;
+ }
+
+ public Builder configs(Map configs) {
+
+ if (configs != null && !configs.isEmpty()) {
+ this.configs.putAll(configs);
+ }
+ this.configs = configs;
+ return this;
+ }
+
+ public Builder components(List components) {
+
+ if (components != null && !components.isEmpty()) {
+ this.components.addAll(components);
+ }
+ this.components = components;
+ return this;
+ }
+
+ public Builder action(ActionDTO action) {
+
+ this.action = action;
+ return this;
+ }
+
+ public ComponentDTO build() {
+
+ return new ComponentDTO(this);
+ }
+ }
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/DataDTO.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/DataDTO.java
new file mode 100644
index 000000000000..83328a121865
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/DataDTO.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * 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.carbon.identity.user.registration.mgt.model;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * DTO class for component and step data.
+ */
+public class DataDTO implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+ private ActionDTO action;
+ private String url;
+ private List components = new ArrayList<>();
+
+ public DataDTO() {
+
+ }
+
+ private DataDTO(Builder builder) {
+
+ this.components = builder.components;
+ this.action = builder.action;
+ this.url = builder.url;
+ }
+
+ public List getComponents() {
+
+ return components;
+ }
+
+ public void addComponent(ComponentDTO component) {
+
+ this.components.add(component);
+ }
+
+ public ActionDTO getAction() {
+
+ return action;
+ }
+
+ public void setAction(ActionDTO action) {
+
+ this.action = action;
+ }
+
+ public String getUrl() {
+
+ return url;
+ }
+
+ public void setUrl(String url) {
+
+ this.url = url;
+ }
+
+ /**
+ * Builder class to build {@link DataDTO} objects.
+ */
+ public static class Builder {
+
+ private final List components = new ArrayList<>();
+ private ActionDTO action;
+ private String url;
+
+ public Builder components(List components) {
+
+ if (components != null && !components.isEmpty()) {
+ this.components.addAll(components);
+ }
+ return this;
+ }
+
+ public Builder action(ActionDTO action) {
+
+ this.action = action;
+ return this;
+ }
+
+ public Builder url(String url) {
+
+ this.url = url;
+ return this;
+ }
+
+ public DataDTO build() {
+
+ return new DataDTO(this);
+ }
+ }
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ExecutorDTO.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ExecutorDTO.java
new file mode 100644
index 000000000000..941c6942737d
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ExecutorDTO.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * 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.carbon.identity.user.registration.mgt.model;
+
+import java.io.Serializable;
+
+/**
+ * DTO class for Executor.
+ */
+public class ExecutorDTO implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+ private String name;
+ private String idpName;
+
+ public ExecutorDTO() {
+
+ }
+
+ private ExecutorDTO(Builder builder) {
+
+ this.name = builder.name;
+ this.idpName = builder.idpName;
+ }
+
+ public ExecutorDTO(String name) {
+
+ this.name = name;
+ }
+
+ public ExecutorDTO(String name, String idpName) {
+
+ this.name = name;
+ this.idpName = idpName;
+ }
+
+ public String getName() {
+
+ return name;
+ }
+
+ public void setName(String name) {
+
+ this.name = name;
+ }
+
+ public String getIdpName() {
+
+ return idpName;
+ }
+
+ public void setIdpName(String idpName) {
+
+ this.idpName = idpName;
+ }
+
+ /**
+ * Builder class to build {@link ExecutorDTO}.
+ */
+ public static class Builder {
+
+ private String name;
+ private String idpName;
+
+ public Builder name(String name) {
+
+ this.name = name;
+ return this;
+ }
+
+ public Builder idpName(String idpName) {
+
+ this.idpName = idpName;
+ return this;
+ }
+
+ public ExecutorDTO build() {
+
+ return new ExecutorDTO(this);
+ }
+ }
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/NodeConfig.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/NodeConfig.java
new file mode 100644
index 000000000000..2df9f1a39144
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/NodeConfig.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * 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.carbon.identity.user.registration.mgt.model;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is responsible for holding the node configuration.
+ */
+public class NodeConfig implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+ private final List edges = new ArrayList<>();
+ private String id;
+ private String type;
+ private boolean isFirstNode;
+ private String nextNodeId;
+ private String previousNodeId;
+ private ExecutorDTO executorConfig;
+
+ public NodeConfig(Builder builder) {
+
+ this.id = builder.id;
+ this.type = builder.type;
+ this.isFirstNode = builder.isFirstNode;
+ this.nextNodeId = builder.nextNodeId;
+ this.previousNodeId = builder.previousNodeId;
+ this.executorConfig = builder.executorConfig;
+ this.edges.addAll(builder.edges);
+ }
+
+ public String getId() {
+
+ return id;
+ }
+
+ public void setId(String id) {
+
+ this.id = id;
+ }
+
+ public String getType() {
+
+ return type;
+ }
+
+ public void setType(String type) {
+
+ this.type = type;
+ }
+
+ public boolean isFirstNode() {
+
+ return isFirstNode;
+ }
+
+ public void setFirstNode(boolean firstNode) {
+
+ isFirstNode = firstNode;
+ }
+
+ public ExecutorDTO getExecutorConfig() {
+
+ return executorConfig;
+ }
+
+ public void setExecutorConfig(ExecutorDTO executorConfig) {
+
+ this.executorConfig = executorConfig;
+ }
+
+ public String getNextNodeId() {
+
+ return nextNodeId;
+ }
+
+ public void setNextNodeId(String nextNodeId) {
+
+ this.nextNodeId = nextNodeId;
+ }
+
+ public String getPreviousNodeId() {
+
+ return previousNodeId;
+ }
+
+ public void setPreviousNodeId(String previousNodeId) {
+
+ this.previousNodeId = previousNodeId;
+ }
+
+ public List getEdges() {
+
+ return edges;
+ }
+
+ public void addEdge(NodeEdge edge) {
+
+ this.edges.add(edge);
+ }
+
+ public static class Builder {
+
+ private final List edges = new ArrayList<>();
+ private String id;
+ private String type;
+ private boolean isFirstNode;
+ private String nextNodeId = null;
+ private String previousNodeId = null;
+ private ExecutorDTO executorConfig = null;
+
+ public Builder id(String id) {
+
+ this.id = id;
+ return this;
+ }
+
+ public Builder type(String type) {
+
+ this.type = type;
+ return this;
+ }
+
+ public Builder isFirstNode(boolean isFirstNode) {
+
+ this.isFirstNode = isFirstNode;
+ return this;
+ }
+
+ public Builder nextNodeId(String nextNodeId) {
+
+ this.nextNodeId = nextNodeId;
+ return this;
+ }
+
+ public Builder previousNodeId(String previousNodeId) {
+
+ this.previousNodeId = previousNodeId;
+ return this;
+ }
+
+ public Builder executorConfig(ExecutorDTO executorConfig) {
+
+ this.executorConfig = executorConfig;
+ return this;
+ }
+
+ public Builder edges(List edges) {
+
+ this.edges.addAll(edges);
+ return this;
+ }
+
+ public NodeConfig build() {
+
+ return new NodeConfig(this);
+ }
+ }
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/NodeEdge.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/NodeEdge.java
new file mode 100644
index 000000000000..4fdecc7f03ed
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/NodeEdge.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * 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.carbon.identity.user.registration.mgt.model;
+
+import java.io.Serializable;
+
+/**
+ * DTO class for NodeEdge in the registration graph.
+ */
+public class NodeEdge implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+ String sourceNodeId;
+ String targetNodeId;
+ String triggeringActionId;
+
+ public NodeEdge(String sourceNodeId, String targetNodeId, String triggeringActionId) {
+
+ this.sourceNodeId = sourceNodeId;
+ this.targetNodeId = targetNodeId;
+ this.triggeringActionId = triggeringActionId;
+ }
+
+ public String getSourceNodeId() {
+
+ return sourceNodeId;
+ }
+
+ public void setSourceNodeId(String sourceNodeId) {
+
+ this.sourceNodeId = sourceNodeId;
+ }
+
+ public String getTargetNodeId() {
+
+ return targetNodeId;
+ }
+
+ public void setTargetNodeId(String targetNodeId) {
+
+ this.targetNodeId = targetNodeId;
+ }
+
+ public String getTriggeringActionId() {
+
+ return triggeringActionId;
+ }
+
+ public void setTriggeringActionId(String triggeringActionId) {
+
+ this.triggeringActionId = triggeringActionId;
+ }
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/RegistrationFlowDTO.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/RegistrationFlowDTO.java
new file mode 100644
index 000000000000..5dc3404b1009
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/RegistrationFlowDTO.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * 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.carbon.identity.user.registration.mgt.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * DTO class for RegistrationFlow.
+ */
+public class RegistrationFlowDTO {
+
+ private List steps = new ArrayList<>();
+
+ public List getSteps() {
+
+ return steps;
+ }
+
+ public void setSteps(List steps) {
+
+ this.steps = steps;
+ }
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/RegistrationGraphConfig.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/RegistrationGraphConfig.java
new file mode 100644
index 000000000000..55dce7cd58e0
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/RegistrationGraphConfig.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * 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.carbon.identity.user.registration.mgt.model;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class is responsible for holding the registration flow configuration.
+ */
+public class RegistrationGraphConfig implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+ private final Map nodeConfigs = new HashMap<>();
+ private final Map nodePageMappings = new HashMap<>();
+ private String id;
+ private String firstNodeId;
+
+ public String getId() {
+
+ return id;
+ }
+
+ public void setId(String id) {
+
+ this.id = id;
+ }
+
+ public String getFirstNodeId() {
+
+ return firstNodeId;
+ }
+
+ public void setFirstNodeId(String firstNodeId) {
+
+ this.firstNodeId = firstNodeId;
+ }
+
+ public Map getNodeConfigs() {
+
+ return nodeConfigs;
+ }
+
+ public void setNodeConfigs(Map nodeConfigs) {
+
+ this.nodeConfigs.putAll(nodeConfigs);
+ }
+
+ public void addNodeConfig(NodeConfig node) {
+
+ this.nodeConfigs.put(node.getId(), node);
+ }
+
+ public Map getNodePageMappings() {
+
+ return nodePageMappings;
+ }
+
+ public void setNodePageMappings(Map stepContent) {
+
+ this.nodePageMappings.putAll(stepContent);
+ }
+
+ public void addNodePageMapping(String nodeId, StepDTO stepDTO) {
+
+ this.nodePageMappings.put(nodeId, stepDTO);
+ }
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/StepDTO.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/StepDTO.java
new file mode 100644
index 000000000000..137e38083a2a
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/StepDTO.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * 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.carbon.identity.user.registration.mgt.model;
+
+import java.io.Serializable;
+
+/**
+ * DTO class for Step.
+ */
+public class StepDTO implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+ private String id;
+ private String type;
+ private double coordinateX;
+ private double coordinateY;
+ private double width;
+ private double height;
+ private DataDTO data;
+
+ public StepDTO() {
+
+ }
+
+ private StepDTO(Builder builder) {
+
+ this.id = builder.id;
+ this.type = builder.type;
+ this.coordinateX = builder.coordinateX;
+ this.coordinateY = builder.coordinateY;
+ this.width = builder.width;
+ this.height = builder.height;
+ this.data = builder.data;
+ }
+
+ public String getId() {
+
+ return id;
+ }
+
+ public void setId(String id) {
+
+ this.id = id;
+ }
+
+ public String getType() {
+
+ return type;
+ }
+
+ public void setType(String type) {
+
+ this.type = type;
+ }
+
+ public double getCoordinateX() {
+
+ return coordinateX;
+ }
+
+ public void setCoordinateX(double coordinateX) {
+
+ this.coordinateX = coordinateX;
+ }
+
+ public double getCoordinateY() {
+
+ return coordinateY;
+ }
+
+ public void setCoordinateY(double coordinateY) {
+
+ this.coordinateY = coordinateY;
+ }
+
+ public double getWidth() {
+
+ return width;
+ }
+
+ public void setWidth(double width) {
+
+ this.width = width;
+ }
+
+ public double getHeight() {
+
+ return height;
+ }
+
+ public void setHeight(double height) {
+
+ this.height = height;
+ }
+
+ public DataDTO getData() {
+
+ return data;
+ }
+
+ public void setData(DataDTO data) {
+
+ this.data = data;
+ }
+
+ /**
+ * Builder class to build {@link StepDTO}.
+ */
+ public static class Builder {
+
+ private String id;
+ private String type;
+ private double coordinateX;
+ private double coordinateY;
+ private double width;
+ private double height;
+ private DataDTO data;
+
+ public Builder id(String id) {
+
+ this.id = id;
+ return this;
+ }
+
+ public Builder type(String type) {
+
+ this.type = type;
+ return this;
+ }
+
+ public Builder coordinateX(double coordinateX) {
+
+ this.coordinateX = coordinateX;
+ return this;
+ }
+
+ public Builder coordinateY(double coordinateY) {
+
+ this.coordinateY = coordinateY;
+ return this;
+ }
+
+ public Builder width(double width) {
+
+ this.width = width;
+ return this;
+ }
+
+ public Builder height(double height) {
+
+ this.height = height;
+ return this;
+ }
+
+ public Builder data(DataDTO data) {
+
+ this.data = data;
+ return this;
+ }
+
+ public StepDTO build() {
+
+ return new StepDTO(this);
+ }
+ }
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ValidationDTO.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ValidationDTO.java
new file mode 100644
index 000000000000..6ab666abb7bc
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ValidationDTO.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * 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.carbon.identity.user.registration.mgt.model;
+
+/*
+ * DTO class for Validation.
+ */
+public class ValidationDTO {
+
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/utils/GraphBuilder.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/utils/GraphBuilder.java
new file mode 100644
index 000000000000..1417f276d8c1
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/utils/GraphBuilder.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * 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.carbon.identity.user.registration.mgt.utils;
+
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.ActionTypes.EXECUTOR;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.ActionTypes.NEXT;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.COMPLETE;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.ComponentTypes.BUTTON;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.ComponentTypes.FORM;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages.ERROR_CODE_ACTION_DATA_NOT_FOUND;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages.ERROR_CODE_COMPONENT_DATA_NOT_FOUND;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages.ERROR_CODE_EXECUTOR_INFO_NOT_FOUND;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages.ERROR_CODE_INVALID_ACTION_FOR_BUTTON;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages.ERROR_CODE_INVALID_ACTION_TYPE;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages.ERROR_CODE_INVALID_NEXT_STEP;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages.ERROR_CODE_INVALID_NODE;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages.ERROR_CODE_MULTIPLE_STEP_EXECUTORS;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages.ERROR_CODE_NEXT_ACTION_NOT_FOUND;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages.ERROR_CODE_UNSUPPORTED_ACTION_TYPE;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.ExecutorTypes.USER_ONBOARDING;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.NodeTypes.DECISION;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.NodeTypes.PROMPT_ONLY;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.NodeTypes.TASK_EXECUTION;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.StepTypes.REDIRECTION;
+import static org.wso2.carbon.identity.user.registration.mgt.Constants.StepTypes.VIEW;
+import static org.wso2.carbon.identity.user.registration.mgt.utils.RegistrationMgtUtils.handleClientException;
+import static org.wso2.carbon.identity.user.registration.mgt.utils.RegistrationMgtUtils.handleServerException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.wso2.carbon.identity.user.registration.mgt.Constants;
+import org.wso2.carbon.identity.user.registration.mgt.exception.RegistrationClientException;
+import org.wso2.carbon.identity.user.registration.mgt.exception.RegistrationFrameworkException;
+import org.wso2.carbon.identity.user.registration.mgt.model.ActionDTO;
+import org.wso2.carbon.identity.user.registration.mgt.model.ComponentDTO;
+import org.wso2.carbon.identity.user.registration.mgt.model.ExecutorDTO;
+import org.wso2.carbon.identity.user.registration.mgt.model.NodeConfig;
+import org.wso2.carbon.identity.user.registration.mgt.model.NodeEdge;
+import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationFlowDTO;
+import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationGraphConfig;
+import org.wso2.carbon.identity.user.registration.mgt.model.StepDTO;
+
+/**
+ * This class is responsible for building the registration flow graph.
+ */
+public class GraphBuilder {
+
+ private static final Log LOG = LogFactory.getLog(GraphBuilder.class);
+
+ private GraphBuilder() {
+
+ }
+
+ /**
+ * Converts the registration flow DTO to a registration graph configuration.
+ *
+ * @param flowDTO The registration flow DTO.
+ * @return The registration graph configuration.
+ * @throws RegistrationFrameworkException If an error occurs while converting the flow.
+ */
+ public static RegistrationGraphConfig convert(RegistrationFlowDTO flowDTO)
+ throws RegistrationFrameworkException {
+
+ RegistrationGraphConfig flowConfig = new RegistrationGraphConfig();
+ flowConfig.setId(UUID.randomUUID().toString());
+
+ Map nodeMap = new HashMap<>();
+ Map stepContentMap = new HashMap<>();
+ List nodeEdges = new ArrayList<>();
+
+ String endNodeId = handleEndNode(nodeMap);
+
+ for (StepDTO step : flowDTO.getSteps()) {
+ if (step.getData() == null) {
+ throw handleClientException(Constants.ErrorMessages.ERROR_CODE_STEP_DATA_NOT_FOUND, step.getId());
+ }
+ NodeConfig stepNode;
+ if (VIEW.equals(step.getType())) {
+ stepNode = processViewStep(step, nodeMap, nodeEdges, endNodeId);
+ } else if (REDIRECTION.equals(step.getType())) {
+ stepNode = processRedirectionStep(step, nodeMap, nodeEdges, endNodeId);
+ } else {
+ throw handleClientException(Constants.ErrorMessages.ERROR_CODE_UNSUPPORTED_STEP_TYPE, step.getType());
+ }
+ stepContentMap.put(step.getId(), step);
+ setFirstNodeIfNeeded(flowConfig, stepNode);
+ }
+
+ updateNodeMappings(nodeMap, nodeEdges);
+ flowConfig.setNodeConfigs(nodeMap);
+ flowConfig.setNodePageMappings(stepContentMap);
+ return flowConfig;
+ }
+
+ private static String handleEndNode(Map nodeMap) {
+
+ NodeConfig userOnboardingNode = createUserOnboardingNode();
+ nodeMap.put(userOnboardingNode.getId(), userOnboardingNode);
+ return userOnboardingNode.getId();
+ }
+
+ private static NodeConfig processRedirectionStep(StepDTO step, Map nodeMap,
+ List nodeMappings, String endNodeId)
+ throws RegistrationFrameworkException {
+
+ ActionDTO action = step.getData().getAction();
+ if (action == null) {
+ throw handleClientException(ERROR_CODE_ACTION_DATA_NOT_FOUND, step.getId());
+ }
+ if (!EXECUTOR.equals(action.getType())) {
+ throw handleClientException(ERROR_CODE_INVALID_ACTION_TYPE, action.getType(), step.getId());
+ }
+
+ NodeConfig redirectionNode = createTaskExecutionNode(step.getId(), action.getExecutor());
+ String nextNodeId = COMPLETE.equals(action.getNextId()) ? endNodeId : action.getNextId();
+ nodeMap.put(redirectionNode.getId(), redirectionNode);
+ nodeMappings.add(new NodeEdge(redirectionNode.getId(), nextNodeId, null));
+ return redirectionNode;
+ }
+
+ private static NodeConfig processViewStep(StepDTO step, Map nodeMap, List nodeEdges,
+ String endNodeId) throws RegistrationFrameworkException {
+
+ List stepNodes = new ArrayList<>();
+ List components = step.getData().getComponents();
+ if (components == null || components.isEmpty()) {
+ throw handleClientException(ERROR_CODE_COMPONENT_DATA_NOT_FOUND, step.getId());
+ }
+ for (ComponentDTO component : components) {
+ processComponent(component, stepNodes, step.getId());
+ }
+ return handleTempNodesInStep(stepNodes, step, nodeMap, nodeEdges, endNodeId);
+ }
+
+ private static void processComponent(ComponentDTO component, List stepNodes, String stepId)
+ throws RegistrationFrameworkException {
+
+ if (FORM.equals(component.getType())) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Processing form component: " + component.getId());
+ }
+ for (ComponentDTO subComponent : component.getComponents()) {
+ processComponent(subComponent, stepNodes, stepId);
+ }
+ } else if (BUTTON.equals(component.getType())) {
+ validateStepActions(component.getAction(), stepNodes, component.getId(), stepId);
+ stepNodes.add(createNodeFromAction(component.getAction(), component.getId()));
+ }
+ }
+
+ private static void validateStepActions(ActionDTO action, List stepNodes, String id, String stepId)
+ throws RegistrationFrameworkException {
+
+ if (action == null) {
+ throw handleClientException(ERROR_CODE_INVALID_ACTION_FOR_BUTTON, id);
+ }
+ if (action.getNextId() == null) {
+ throw handleClientException(ERROR_CODE_NEXT_ACTION_NOT_FOUND, id);
+ }
+ if (EXECUTOR.equals(action.getType())) {
+ if (action.getExecutor() == null) {
+ throw handleClientException(ERROR_CODE_EXECUTOR_INFO_NOT_FOUND, id);
+ }
+ if (stepNodes.stream().anyMatch(nodeConfig -> (TASK_EXECUTION.equals(nodeConfig.getType())))) {
+ throw handleClientException(ERROR_CODE_MULTIPLE_STEP_EXECUTORS, stepId);
+ }
+ }
+ }
+
+ private static NodeConfig createNodeFromAction(ActionDTO action, String componentId)
+ throws RegistrationClientException {
+
+ NodeConfig tempNodeInComponent;
+ if (NEXT.equals(action.getType())) {
+ tempNodeInComponent = createPagePromptNode(componentId);
+ } else if (EXECUTOR.equals(action.getType())) {
+ tempNodeInComponent = createTaskExecutionNode(componentId, action.getExecutor());
+ } else {
+ throw handleClientException(ERROR_CODE_UNSUPPORTED_ACTION_TYPE, action.getType(), componentId);
+ }
+ tempNodeInComponent.setNextNodeId(action.getNextId());
+ return tempNodeInComponent;
+ }
+
+ private static NodeConfig handleTempNodesInStep(List tempNodesInStep, StepDTO step,
+ Map nodeMap, List nodeMappings,
+ String endNodeId) {
+
+ NodeConfig stepNode = null;
+ if (tempNodesInStep.size() > 1) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Multiple nodes are derived from the step: " + step.getId() + ". Creating a decision node.");
+ }
+ NodeConfig decisionNode = createDecisionNode(step.getId());
+ for (NodeConfig nodeConfig : tempNodesInStep) {
+ String nextNodeId =
+ COMPLETE.equals(nodeConfig.getNextNodeId()) ? endNodeId : nodeConfig.getNextNodeId();
+ if (TASK_EXECUTION.equals(nodeConfig.getType())) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("A node with an execution found in the step. Therefore adding it to the node list " +
+ "with id, " + nodeConfig.getId());
+ }
+ nodeConfig.setNextNodeId(null);
+ nodeMap.put(nodeConfig.getId(), nodeConfig);
+ nodeMappings.add(new NodeEdge(nodeConfig.getId(), nextNodeId, null));
+ nodeMappings.add(new NodeEdge(decisionNode.getId(), nodeConfig.getId(), nodeConfig.getId()));
+ } else {
+ // Edge from decision node to the next node derived from NEXT actions.
+ nodeMappings.add(new NodeEdge(decisionNode.getId(), nextNodeId, nodeConfig.getId()));
+ }
+ }
+ stepNode = decisionNode;
+ } else if (tempNodesInStep.size() == 1) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Only one node derived from the step: " + step.getId() + ". Adding it to the node list.");
+ }
+
+ NodeConfig tempNode = tempNodesInStep.get(0);
+
+ String nextNodeId = COMPLETE.equals(tempNode.getNextNodeId()) ? endNodeId : tempNode.getNextNodeId();
+ stepNode = new NodeConfig.Builder()
+ .id(step.getId())
+ .type(tempNode.getType())
+ .executorConfig(tempNode.getExecutorConfig())
+ .build();
+ nodeMappings.add(new NodeEdge(stepNode.getId(), nextNodeId, tempNode.getId()));
+ }
+ nodeMap.put(step.getId(), stepNode);
+ return stepNode;
+ }
+
+ private static void setFirstNodeIfNeeded(RegistrationGraphConfig flowConfig, NodeConfig nodeConfig) {
+
+ if (flowConfig.getFirstNodeId() == null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(
+ "Setting node: " + nodeConfig.getId() + " as the first node in the flow " + flowConfig.getId());
+ }
+ flowConfig.setFirstNodeId(nodeConfig.getId());
+ nodeConfig.setFirstNode(true);
+ }
+ }
+
+ private static void updateNodeMappings(Map nodeMap, List nodeMappings)
+ throws RegistrationFrameworkException {
+
+ for (NodeEdge edge : nodeMappings) {
+ String nodeId = edge.getSourceNodeId();
+ String nextNodeId = edge.getTargetNodeId();
+
+ if (!nodeMap.containsKey(nodeId)) {
+ throw handleServerException(ERROR_CODE_INVALID_NODE, nodeId);
+ }
+ if (!nodeMap.containsKey(nextNodeId)) {
+ throw handleClientException(ERROR_CODE_INVALID_NEXT_STEP, nextNodeId);
+ }
+ nodeMap.get(nodeId).addEdge(edge);
+ }
+ }
+
+ private static NodeConfig createTaskExecutionNode(String id, ExecutorDTO executorDTO) {
+
+ NodeConfig node = new NodeConfig.Builder()
+ .id(id)
+ .type(TASK_EXECUTION)
+ .executorConfig(executorDTO)
+ .build();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Created a task execution node " + id + " with executor " + executorDTO.getName() + ".");
+ }
+ return node;
+ }
+
+ private static NodeConfig createDecisionNode(String id) {
+
+ NodeConfig nodeConfig = new NodeConfig.Builder()
+ .id(id)
+ .type(DECISION)
+ .build();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Created a decision node " + id + ".");
+ }
+ return nodeConfig;
+ }
+
+ private static NodeConfig createUserOnboardingNode() {
+
+ NodeConfig nodeConfig = new NodeConfig.Builder()
+ .id(UUID.randomUUID().toString())
+ .type(TASK_EXECUTION)
+ .executorConfig(new ExecutorDTO(USER_ONBOARDING))
+ .build();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Created a node with id " + nodeConfig.getId() + " for user onboarding.");
+ }
+ return nodeConfig;
+ }
+
+ private static NodeConfig createPagePromptNode(String id) {
+
+ NodeConfig nodeConfig = new NodeConfig.Builder()
+ .id(id)
+ .type(PROMPT_ONLY)
+ .build();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Created a node with id " + nodeConfig.getId() + " to prompt a page.");
+ }
+ return nodeConfig;
+ }
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/utils/RegistrationMgtUtils.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/utils/RegistrationMgtUtils.java
new file mode 100644
index 000000000000..2b9430f1f4e1
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/utils/RegistrationMgtUtils.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * 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.carbon.identity.user.registration.mgt.utils;
+
+import static org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils.ENABLE_V2_AUDIT_LOGS;
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.lang.StringUtils;
+import org.wso2.carbon.CarbonConstants;
+import org.wso2.carbon.context.CarbonContext;
+import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils;
+import org.wso2.carbon.identity.core.util.IdentityUtil;
+import org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages;
+import org.wso2.carbon.identity.user.registration.mgt.exception.RegistrationClientException;
+import org.wso2.carbon.identity.user.registration.mgt.exception.RegistrationServerException;
+import org.wso2.carbon.user.core.util.UserCoreUtil;
+import org.wso2.carbon.utils.multitenancy.MultitenantUtils;
+
+public class RegistrationMgtUtils {
+
+ private RegistrationMgtUtils() {
+
+ }
+
+ /**
+ * Handle the registration flow management server exceptions.
+ *
+ * @param error Error message.
+ * @param e Throwable.
+ * @param data The error message data.
+ * @return RegistrationServerException.
+ */
+ public static RegistrationServerException handleServerException(ErrorMessages error, Throwable e, Object... data) {
+
+ String description = error.getDescription();
+ if (ArrayUtils.isNotEmpty(data)) {
+ description = String.format(description, data);
+ }
+ return new RegistrationServerException(error.getCode(), error.getMessage(), description, e);
+ }
+
+ /**
+ * Handle the registration flow management server exceptions.
+ *
+ * @param error Error message.
+ * @param data The error message data.
+ * @return RegistrationServerException.
+ */
+ public static RegistrationServerException handleServerException(ErrorMessages error, Object... data) {
+
+ String description = error.getDescription();
+ if (ArrayUtils.isNotEmpty(data)) {
+ description = String.format(description, data);
+ }
+ return new RegistrationServerException(error.getCode(), error.getMessage(), description);
+ }
+
+ /**
+ * Handle the registration flow management client exceptions.
+ *
+ * @param error Error message.
+ * @param e Throwable.
+ * @param data The error message data.
+ * @return RegistrationClientException.
+ */
+ public static RegistrationClientException handleClientException(ErrorMessages error, Throwable e, Object... data) {
+
+ String description = error.getDescription();
+ if (ArrayUtils.isNotEmpty(data)) {
+ description = String.format(description, data);
+ }
+ return new RegistrationClientException(error.getCode(), error.getMessage(), description, e);
+ }
+
+ /**
+ * Handle the registration flow management client exceptions.
+ *
+ * @param error Error message.
+ * @param data The error message data.
+ * @return RegistrationClientException.
+ */
+ public static RegistrationClientException handleClientException(ErrorMessages error, Object... data) {
+
+ String description = error.getDescription();
+ if (ArrayUtils.isNotEmpty(data)) {
+ description = String.format(description, data);
+ }
+ return new RegistrationClientException(error.getCode(), error.getMessage(), description);
+ }
+
+ /**
+ * Check whether the v2 audit logs are enabled.
+ *
+ * @return true if v2 audit logs are enabled.
+ */
+ public static boolean isEnableV2AuditLogs() {
+
+ return Boolean.parseBoolean(System.getProperty(ENABLE_V2_AUDIT_LOGS));
+ }
+
+ /**
+ * Get the initiator for audit logs.
+ *
+ * @return Initiator id despite masking.
+ */
+ public static String getInitiatorId() {
+
+ String initiator = null;
+ String username = MultitenantUtils.getTenantAwareUsername(getUser());
+ String tenantDomain = MultitenantUtils.getTenantDomain(getUser());
+ if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(tenantDomain)) {
+ initiator = IdentityUtil.getInitiatorId(username, tenantDomain);
+ }
+ if (StringUtils.isBlank(initiator)) {
+ if (username.equals(CarbonConstants.REGISTRY_SYSTEM_USERNAME)) {
+ // If the initiator is wso2.system, we need not to mask the username.
+ return LoggerUtils.Initiator.System.name();
+ }
+ initiator = LoggerUtils.getMaskedContent(getUser());
+ }
+ return initiator;
+ }
+
+ /**
+ * To get the current user, who is doing the current task.
+ *
+ * @return current logged-in user.
+ */
+ private static String getUser() {
+
+ String user = CarbonContext.getThreadLocalCarbonContext().getUsername();
+ if (StringUtils.isNotEmpty(user)) {
+ user = UserCoreUtil
+ .addTenantDomainToEntry(user, CarbonContext.getThreadLocalCarbonContext().getTenantDomain());
+ } else {
+ user = CarbonConstants.REGISTRY_SYSTEM_USERNAME;
+ }
+ return user;
+ }
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/java/org/wso2/carbon/identity/user/registration/mgt/dao/RegistrationFlowDAOImplTest.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/java/org/wso2/carbon/identity/user/registration/mgt/dao/RegistrationFlowDAOImplTest.java
new file mode 100644
index 000000000000..2a8481f5c49e
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/java/org/wso2/carbon/identity/user/registration/mgt/dao/RegistrationFlowDAOImplTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2025, 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.carbon.identity.user.registration.mgt.dao;
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.dbcp.BasicDataSource;
+import org.apache.commons.lang.StringUtils;
+import org.mockito.MockedStatic;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import org.wso2.carbon.identity.core.util.IdentityDatabaseUtil;
+import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationFlowDTO;
+import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationGraphConfig;
+import org.wso2.carbon.identity.user.registration.mgt.utils.GraphBuilder;
+
+import java.io.File;
+import java.nio.file.Paths;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.mockito.Mockito.mockStatic;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+/**
+ * Test class for RegistrationFlowDAOImpl.
+ */
+public class RegistrationFlowDAOImplTest {
+
+ private static final String DB_NAME = "registration_flow_mgt_dao_db";
+ private RegistrationFlowDAOImpl daoImpl;
+ private static final Map dataSourceMap = new HashMap<>();
+
+ @BeforeClass
+ public void setUp() throws Exception {
+
+ daoImpl = new RegistrationFlowDAOImpl();
+ initiateH2Database(getFilePath("identity.sql"));
+ }
+
+ @AfterClass
+ public void tearDown() throws Exception {
+
+ closeH2Database();
+ }
+
+ @DataProvider
+ public Object[][] registrationFlowData() {
+ return new Object[][]{
+ {createSampleRegistrationGraphConfig(), 1, "SampleFlow"}
+ };
+ }
+
+ @Test(dataProvider = "registrationFlowData")
+ public void testUpdateDefaultRegistrationFlowByTenant(RegistrationGraphConfig regFlowConfig, int tenantId,
+ String flowName) throws Exception {
+
+ try (MockedStatic identityDatabaseUtil = mockStatic(IdentityDatabaseUtil.class)) {
+ identityDatabaseUtil.when(IdentityDatabaseUtil::getDataSource)
+ .thenReturn(dataSourceMap.get(DB_NAME));
+ daoImpl.updateDefaultRegistrationFlowByTenant(regFlowConfig, tenantId, flowName);
+ RegistrationFlowDTO flowDTO = daoImpl.getDefaultRegistrationFlowByTenant(tenantId);
+ assertNotNull(flowDTO);
+ }
+ }
+
+ @Test(dataProvider = "registrationFlowData")
+ public void testGetDefaultRegistrationFlowByTenant(RegistrationGraphConfig regFlowConfig, int tenantId,
+ String flowName) throws Exception {
+
+ try (MockedStatic identityDatabaseUtil = mockStatic(IdentityDatabaseUtil.class)) {
+ identityDatabaseUtil.when(IdentityDatabaseUtil::getDataSource)
+ .thenReturn(dataSourceMap.get(DB_NAME));
+ daoImpl.updateDefaultRegistrationFlowByTenant(regFlowConfig, tenantId, flowName);
+ RegistrationFlowDTO flowDTO = daoImpl.getDefaultRegistrationFlowByTenant(tenantId);
+ assertNotNull(flowDTO);
+ assertEquals(flowDTO.getSteps().size(), 3);
+ assertEquals(flowDTO.getSteps().get(0).getId(), "step_1");
+ }
+ }
+
+ private static RegistrationGraphConfig createSampleRegistrationGraphConfig() {
+
+ try {
+ ObjectMapper objectMapper = new ObjectMapper();
+ objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ RegistrationFlowDTO flowDTO = objectMapper.readValue(new File(getFilePath("reg_flow.json")),
+ RegistrationFlowDTO.class);
+ return GraphBuilder.convert(flowDTO);
+ } catch (Exception e) {
+ throw new RuntimeException("Error while reading the JSON file.", e);
+ }
+ }
+
+ private static Connection getConnection() throws SQLException {
+
+ if (dataSourceMap.get(DB_NAME) != null) {
+ return dataSourceMap.get(DB_NAME).getConnection();
+ }
+ throw new RuntimeException("No datasource initiated for database: " + DB_NAME);
+ }
+
+ /**
+ * Initiate H2 database.
+ *
+ * @param scriptPath Path to the database script.
+ * @throws Exception Error when initiating H2 database.
+ */
+ private void initiateH2Database(String scriptPath) throws Exception {
+
+ BasicDataSource dataSource = new BasicDataSource();
+ dataSource.setDriverClassName("org.h2.Driver");
+ dataSource.setUsername("username");
+ dataSource.setPassword("password");
+ dataSource.setUrl("jdbc:h2:mem:test" + DB_NAME);
+ dataSource.setTestOnBorrow(true);
+ dataSource.setValidationQuery("select 1");
+ try (Connection connection = dataSource.getConnection()) {
+ connection.createStatement().executeUpdate("RUNSCRIPT FROM '" + scriptPath + "'");
+ }
+ dataSourceMap.put(DB_NAME, dataSource);
+ }
+
+ /**
+ * Close H2 database.
+ *
+ * @throws Exception Error when closing H2 database.
+ */
+ public static void closeH2Database() throws Exception {
+
+ BasicDataSource dataSource = dataSourceMap.get(DB_NAME);
+ if (dataSource != null) {
+ dataSource.close();
+ }
+ }
+
+ /**
+ * Get the path to the database script.
+ *
+ * @return Path to the database script.
+ */
+ private static String getFilePath(String filename) {
+
+ if (StringUtils.isNotBlank(filename)) {
+ return Paths.get(System.getProperty("user.dir"), "src", "test", "resources", filename).toString();
+ }
+ throw new IllegalArgumentException("DB Script file name cannot be empty.");
+ }
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/java/org/wso2/carbon/identity/user/registration/mgt/utils/GraphBuilderTest.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/java/org/wso2/carbon/identity/user/registration/mgt/utils/GraphBuilderTest.java
new file mode 100644
index 000000000000..7be005c1903d
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/java/org/wso2/carbon/identity/user/registration/mgt/utils/GraphBuilderTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2025, 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.carbon.identity.user.registration.mgt.utils;
+
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.wso2.carbon.identity.user.registration.mgt.Constants;
+import org.wso2.carbon.identity.user.registration.mgt.exception.RegistrationFrameworkException;
+import org.wso2.carbon.identity.user.registration.mgt.model.ActionDTO;
+import org.wso2.carbon.identity.user.registration.mgt.model.ComponentDTO;
+import org.wso2.carbon.identity.user.registration.mgt.model.DataDTO;
+import org.wso2.carbon.identity.user.registration.mgt.model.ExecutorDTO;
+import org.wso2.carbon.identity.user.registration.mgt.model.NodeConfig;
+import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationFlowDTO;
+import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationGraphConfig;
+import org.wso2.carbon.identity.user.registration.mgt.model.StepDTO;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * Test class for GraphBuilder.
+ */
+public class GraphBuilderTest {
+
+ public static final String STEP_1 = "step_qx3a";
+ public static final String STEP_2 = "step_we12";
+ public static final String STEP_3 = "step_sd21";
+ public static final String GOOGLE_IDP_NAME = "Google1";
+ public static final String OIDC_AUTHENTICATOR = "OIDCAuthenticator";
+ public static final String OIDC_IDP = "OIDC_IDP";
+ public static final String GOOGLE_OIDC_AUTHENTICATOR = "GoogleOIDCAuthenticator";
+ public static Map stepIdToNextNodeIdMap = new HashMap<>();
+
+ @BeforeClass
+ public void setUpClass() {
+ }
+
+ @AfterMethod
+ public void tearDown() {
+ }
+
+ @Test
+ public void testConvert() throws RegistrationFrameworkException {
+
+ // Create a sample RegistrationFlowDTO.
+ RegistrationFlowDTO flowDTO = new RegistrationFlowDTO();
+ flowDTO.setSteps(new ArrayList<>());
+
+ Map configs = new HashMap<>();
+ configs.put("key1", "value1");
+ configs.put("key2", "value2");
+
+ ComponentDTO componentDTO1 = new ComponentDTO.Builder()
+ .id("component_2sa1")
+ .type("BUTTON")
+ .configs(configs)
+ .category("SUBMIT")
+ .action(new ActionDTO.Builder()
+ .type(Constants.ActionTypes.NEXT)
+ .nextId(STEP_2)
+ .build())
+ .build();
+
+ // Add a sample StepDTO.
+ StepDTO stepDTO1 = new StepDTO.Builder()
+ .id(STEP_1)
+ .type(Constants.StepTypes.VIEW)
+ .coordinateX(0)
+ .coordinateY(0)
+ .height(100.15)
+ .width(200.25)
+ .data(new DataDTO.Builder()
+ .components(Collections.singletonList(componentDTO1))
+ .build())
+ .build();
+
+ ComponentDTO componentDTO2 = new ComponentDTO.Builder()
+ .id("component_2sa1")
+ .type("BUTTON")
+ .configs(configs)
+ .action(new ActionDTO.Builder()
+ .type(Constants.ActionTypes.EXECUTOR)
+ .executor(new ExecutorDTO.Builder()
+ .name(OIDC_AUTHENTICATOR)
+ .idpName(OIDC_IDP)
+ .build())
+ .nextId(STEP_3)
+ .build())
+ .build();
+
+ StepDTO stepDTO2 = new StepDTO.Builder()
+ .id(STEP_2)
+ .type(Constants.StepTypes.VIEW)
+ .coordinateX(0)
+ .coordinateY(0)
+ .height(100.15)
+ .width(200.25)
+ .data(new DataDTO.Builder()
+ .components(Collections.singletonList(componentDTO2))
+ .build())
+ .build();
+
+ StepDTO stepDTO3 = new StepDTO.Builder()
+ .id(STEP_3)
+ .type(Constants.StepTypes.REDIRECTION)
+ .coordinateX(0)
+ .coordinateY(0)
+ .height(100.15)
+ .width(200.25)
+ .data(new DataDTO.Builder()
+ .action(new ActionDTO.Builder()
+ .type(Constants.ActionTypes.EXECUTOR)
+ .executor(new ExecutorDTO.Builder()
+ .name(GOOGLE_OIDC_AUTHENTICATOR)
+ .idpName(GOOGLE_IDP_NAME)
+ .build())
+ .nextId(Constants.COMPLETE)
+ .build())
+ .build())
+ .build();
+
+ flowDTO.setSteps(Arrays.asList(stepDTO1, stepDTO2, stepDTO3));
+
+ // Convert the flowDTO to a RegistrationGraphConfig.
+ RegistrationGraphConfig graphConfig = GraphBuilder.convert(flowDTO);
+
+ // Assert the converted graphConfig.
+ assertNotNull(graphConfig);
+ assertNotNull(graphConfig.getId());
+ assertEquals(graphConfig.getNodeConfigs().size(), 4);
+ assertEquals(graphConfig.getNodePageMappings().size(), 3);
+ assertEquals(graphConfig.getFirstNodeId(), STEP_1);
+
+ graphConfig.getNodePageMappings().forEach((nodeId, stepDTO) -> {
+ stepIdToNextNodeIdMap.put(stepDTO.getId(), graphConfig.getNodeConfigs().get(nodeId).getNextNodeId());
+ });
+
+ graphConfig.getNodePageMappings().forEach((nodeId, stepDTO) -> {
+ assertNotNull(nodeId);
+ assertNotNull(stepDTO);
+ NodeConfig nodeConfig = graphConfig.getNodeConfigs().get(nodeId);
+ assertNotNull(nodeConfig);
+
+ switch (stepDTO.getId()) {
+ case STEP_1:
+ assertEquals(nodeConfig.getType(), Constants.NodeTypes.PROMPT_ONLY);
+ assertTrue(nodeConfig.isFirstNode());
+ assertEquals(nodeConfig.getNextNodeId(), stepIdToNextNodeIdMap.get(STEP_1));
+ assertNull(nodeConfig.getExecutorConfig());
+ break;
+ case STEP_2:
+ assertEquals(nodeConfig.getType(), Constants.NodeTypes.TASK_EXECUTION);
+ assertEquals(nodeConfig.getExecutorConfig().getName(), OIDC_AUTHENTICATOR);
+ assertEquals(nodeConfig.getExecutorConfig().getIdpName(), OIDC_IDP);
+ assertEquals(nodeConfig.getNextNodeId(), stepIdToNextNodeIdMap.get(STEP_2));
+ break;
+ case STEP_3:
+ assertEquals(nodeConfig.getType(), Constants.NodeTypes.TASK_EXECUTION);
+ assertEquals(nodeConfig.getExecutorConfig().getName(), GOOGLE_OIDC_AUTHENTICATOR);
+ assertEquals(nodeConfig.getExecutorConfig().getIdpName(), GOOGLE_IDP_NAME);
+ assertEquals(nodeConfig.getNextNodeId(), stepIdToNextNodeIdMap.get(STEP_3));
+ break;
+ default:
+ break;
+ }
+ });
+ }
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/resources/identity.sql b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/resources/identity.sql
new file mode 100644
index 000000000000..8ef3067feca9
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/resources/identity.sql
@@ -0,0 +1,56 @@
+CREATE TABLE IDN_FLOW (
+ ID VARCHAR(50) PRIMARY KEY,
+ TENANT_ID INT,
+ FLOW_NAME VARCHAR(255),
+ TYPE VARCHAR(100),
+ IS_DEFAULT BOOLEAN DEFAULT FALSE,
+ UNIQUE (TENANT_ID, FLOW_NAME)
+);
+
+CREATE TABLE IDN_FLOW_NODE (
+ ID INT PRIMARY KEY AUTO_INCREMENT,
+ NODE_ID VARCHAR(50),
+ FLOW_ID VARCHAR(50),
+ NODE_TYPE VARCHAR(100),
+ IS_FIRST_NODE BOOLEAN DEFAULT FALSE,
+ UNIQUE (NODE_ID, FLOW_ID),
+ FOREIGN KEY (FLOW_ID) REFERENCES IDN_FLOW(ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IDN_FLOW_NODE_MAPPING (
+ ID INT PRIMARY KEY AUTO_INCREMENT,
+ FLOW_NODE_ID INT,
+ NEXT_NODE_ID INT,
+ TRIGGERING_ELEMENT VARCHAR(100),
+ FOREIGN KEY (FLOW_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE,
+ FOREIGN KEY (NEXT_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IDN_FLOW_PAGE (
+ ID INT PRIMARY KEY AUTO_INCREMENT,
+ FLOW_ID VARCHAR(50),
+ FLOW_NODE_ID INT,
+ STEP_ID VARCHAR(50),
+ PAGE_CONTENT BLOB,
+ TYPE VARCHAR(100),
+ FOREIGN KEY (FLOW_ID) REFERENCES IDN_FLOW(ID) ON DELETE CASCADE,
+ FOREIGN KEY (FLOW_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IDN_FLOW_PAGE_META (
+ ID INT PRIMARY KEY AUTO_INCREMENT,
+ PAGE_ID INT,
+ COORDINATE_X DOUBLE,
+ COORDINATE_Y DOUBLE,
+ HEIGHT DOUBLE,
+ WIDTH DOUBLE,
+ FOREIGN KEY (PAGE_ID) REFERENCES IDN_FLOW_PAGE(ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IDN_FLOW_NODE_EXECUTOR (
+ ID INT PRIMARY KEY AUTO_INCREMENT,
+ FLOW_NODE_ID INT,
+ EXECUTOR_NAME VARCHAR(255),
+ IDP_NAME INT DEFAULT NULL,
+ FOREIGN KEY (FLOW_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE
+);
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/resources/reg_flow.json b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/resources/reg_flow.json
new file mode 100644
index 000000000000..a85ab57ad0fe
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/resources/reg_flow.json
@@ -0,0 +1,155 @@
+{
+ "steps": [
+ {
+ "id": "step_1",
+ "type": "VIEW",
+ "width": 120,
+ "height": 240,
+ "coordinateX": 0,
+ "coordinateY": 0,
+ "data": {
+ "components": [
+ {
+ "id": "component_232d",
+ "category": "BLOCK",
+ "type": "FORM",
+ "components": [
+ {
+ "id": "element_gd43",
+ "category": "FIELD",
+ "type": "INPUT",
+ "variant": "TEXT",
+ "config": {
+ "type": "text",
+ "hint": "",
+ "label": "Username",
+ "required": true,
+ "placeholder": "Enter your username"
+ }
+ },
+ {
+ "id": "element_23dx",
+ "category": "FIELD",
+ "type": "INPUT",
+ "variant": "PASSWORD",
+ "config": {
+ "name": "password",
+ "type": "password",
+ "hint": "",
+ "label": "Password",
+ "required": true,
+ "placeholder": "Enter your password"
+ }
+ },
+ {
+ "id": "element_56jd",
+ "category": "BUTTON",
+ "type": "BUTTON",
+ "variant": "PRIMARY",
+ "action": {
+ "type": "EXECUTOR",
+ "executor": {
+ "name": "PasswordOnboardExecutor"
+ },
+ "nextId": "step_a5sf"
+ },
+ "config": {
+ "text": "Continue"
+ }
+ }
+ ]
+ },
+ {
+ "id": "element_gd43",
+ "category": "BUTTON",
+ "type": "BUTTON",
+ "variant": "PRIMARY",
+ "action": {
+ "type": "NEXT",
+ "nextId": "step_dfr2"
+ },
+ "config": {
+ "text": "Continue with Google"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "id": "step_a5sf",
+ "type": "VIEW",
+ "width": 120,
+ "height": 240,
+ "coordinateX": 0,
+ "coordinateY": 0,
+ "data": {
+ "components": [
+ {
+ "id": "component_232d",
+ "category": "BLOCK",
+ "type": "FORM",
+ "components": [
+ {
+ "id": "element_gd43",
+ "category": "FIELD",
+ "type": "INPUT",
+ "variant": "TEXT",
+ "config": {
+ "type": "text",
+ "hint": "",
+ "label": "Email",
+ "required": true,
+ "placeholder": "Enter your email"
+ }
+ },
+ {
+ "id": "element_23dx",
+ "category": "FIELD",
+ "type": "INPUT",
+ "variant": "TEXT",
+ "config": {
+ "type": "text",
+ "hint": "",
+ "label": "Phone",
+ "required": true,
+ "placeholder": "Enter your phone"
+ }
+ },
+ {
+ "id": "element_56jd",
+ "category": "BUTTON",
+ "type": "BUTTON",
+ "variant": "PRIMARY",
+ "action": {
+ "type": "NEXT",
+ "nextId": "COMPLETE"
+ },
+ "config": {
+ "text": "Complete Registration"
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "id": "step_dfr2",
+ "type": "REDIRECTION",
+ "width": 120,
+ "height": 240,
+ "coordinateX": 0,
+ "coordinateY": 0,
+ "data": {
+ "action": {
+ "type": "EXECUTOR",
+ "executor": {
+ "name": "GoogleOIDCAuthenticator",
+ "idp_name": "Google"
+ },
+ "nextId": "COMPLETE"
+ }
+ }
+ }
+ ]
+}
diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/resources/testng.xml b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/resources/testng.xml
new file mode 100644
index 000000000000..afd12216f02d
--- /dev/null
+++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/resources/testng.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/registration-framework/pom.xml b/components/registration-framework/pom.xml
new file mode 100644
index 000000000000..2181b481bfbf
--- /dev/null
+++ b/components/registration-framework/pom.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+ org.wso2.carbon.identity.framework
+ identity-framework
+ 7.8.28-SNAPSHOT
+ ../../pom.xml
+
+
+ 4.0.0
+ registration-framework
+ pom
+ WSO2 Carbon - Provisioning Aggregator Module
+
+ This is a Carbon bundle that represents the registration framework.
+
+ http://wso2.org
+
+
+ org.wso2.carbon.identity.user.registration.mgt
+
+
+
diff --git a/components/user-mgt/org.wso2.carbon.identity.user.registration/src/main/java/org/wso2/carbon/identity/user/registration/internal/UserRegistrationDSComponent.java b/components/user-mgt/org.wso2.carbon.identity.user.registration/src/main/java/org/wso2/carbon/identity/user/registration/internal/UserRegistrationDSComponent.java
index d196e1bbd5d9..c3183d5b18f7 100644
--- a/components/user-mgt/org.wso2.carbon.identity.user.registration/src/main/java/org/wso2/carbon/identity/user/registration/internal/UserRegistrationDSComponent.java
+++ b/components/user-mgt/org.wso2.carbon.identity.user.registration/src/main/java/org/wso2/carbon/identity/user/registration/internal/UserRegistrationDSComponent.java
@@ -45,10 +45,10 @@ public static RegistryService getRegistryService() {
}
@Reference(
- name = "registry.service",
- service = org.wso2.carbon.registry.core.service.RegistryService.class,
- cardinality = ReferenceCardinality.MANDATORY,
- policy = ReferencePolicy.DYNAMIC,
+ name = "registry.service",
+ service = org.wso2.carbon.registry.core.service.RegistryService.class,
+ cardinality = ReferenceCardinality.MANDATORY,
+ policy = ReferencePolicy.DYNAMIC,
unbind = "unsetRegistryService")
protected void setRegistryService(RegistryService registryService) {
if (log.isDebugEnabled()) {
@@ -62,10 +62,10 @@ public static RealmService getRealmService() {
}
@Reference(
- name = "user.realmservice.default",
- service = org.wso2.carbon.user.core.service.RealmService.class,
- cardinality = ReferenceCardinality.MANDATORY,
- policy = ReferencePolicy.DYNAMIC,
+ name = "user.realmservice.default",
+ service = org.wso2.carbon.user.core.service.RealmService.class,
+ cardinality = ReferenceCardinality.MANDATORY,
+ policy = ReferencePolicy.DYNAMIC,
unbind = "unsetRealmService")
protected void setRealmService(RealmService realmService) {
if (log.isDebugEnabled()) {
@@ -97,5 +97,5 @@ protected void unsetRealmService(RealmService realmService) {
}
UserRegistrationDSComponent.realmService = null;
}
-}
+}
diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/h2.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/h2.sql
index d92f8d5d75d2..4b1b96bd61ba 100644
--- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/h2.sql
+++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/h2.sql
@@ -1548,6 +1548,63 @@ CREATE TABLE IF NOT EXISTS IDN_PUSH_DEVICE_STORE (
UNIQUE (USER_ID)
);
+CREATE TABLE IDN_FLOW (
+ ID VARCHAR(50) PRIMARY KEY,
+ TENANT_ID INT,
+ FLOW_NAME VARCHAR(255),
+ TYPE VARCHAR(100),
+ IS_DEFAULT BOOLEAN DEFAULT FALSE,
+ UNIQUE (TENANT_ID, FLOW_NAME)
+);
+
+CREATE TABLE IDN_FLOW_NODE (
+ ID INT PRIMARY KEY AUTO_INCREMENT,
+ NODE_ID VARCHAR(50),
+ FLOW_ID VARCHAR(50),
+ NODE_TYPE VARCHAR(100),
+ IS_FIRST_NODE BOOLEAN DEFAULT FALSE,
+ UNIQUE (NODE_ID, FLOW_ID),
+ FOREIGN KEY (FLOW_ID) REFERENCES IDN_FLOW(ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IDN_FLOW_NODE_MAPPING (
+ ID INT PRIMARY KEY AUTO_INCREMENT,
+ FLOW_NODE_ID INT,
+ NEXT_NODE_ID INT,
+ TRIGGERING_ELEMENT VARCHAR(100),
+ FOREIGN KEY (FLOW_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE,
+ FOREIGN KEY (NEXT_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IDN_FLOW_PAGE (
+ ID INT PRIMARY KEY AUTO_INCREMENT,
+ FLOW_ID VARCHAR(50),
+ FLOW_NODE_ID INT,
+ STEP_ID VARCHAR(50),
+ PAGE_CONTENT BLOB,
+ TYPE VARCHAR(100),
+ FOREIGN KEY (FLOW_ID) REFERENCES IDN_FLOW(ID) ON DELETE CASCADE,
+ FOREIGN KEY (FLOW_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IDN_FLOW_PAGE_META (
+ ID INT PRIMARY KEY AUTO_INCREMENT,
+ PAGE_ID INT,
+ COORDINATE_X DOUBLE,
+ COORDINATE_Y DOUBLE,
+ HEIGHT DOUBLE,
+ WIDTH DOUBLE,
+ FOREIGN KEY (PAGE_ID) REFERENCES IDN_FLOW_PAGE(ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IDN_FLOW_NODE_EXECUTOR (
+ ID INT PRIMARY KEY AUTO_INCREMENT,
+ FLOW_NODE_ID INT,
+ EXECUTOR_NAME VARCHAR(255),
+ IDP_NAME INT DEFAULT NULL,
+ FOREIGN KEY (FLOW_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE
+);
+
-- --------------------------- INDEX CREATION -----------------------------
-- IDN_OAUTH2_ACCESS_TOKEN --
CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED);
diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql.sql
index 104ddfb036cd..4ea83f39ebd1 100644
--- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql.sql
+++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql.sql
@@ -1581,6 +1581,68 @@ CREATE TABLE IF NOT EXISTS IDN_PUSH_DEVICE_STORE (
UNIQUE (USER_ID)
) DEFAULT CHARACTER SET latin1 ENGINE INNODB;
+-- Create IDN_FLOW table
+CREATE TABLE IDN_FLOW (
+ ID VARCHAR(50) PRIMARY KEY, -- Unique identifier for the flow
+ TENANT_ID INT, -- References the tenant ID
+ FLOW_NAME VARCHAR(255), -- Human-readable name of the flow
+ TYPE VARCHAR(100), -- Type of flow (e.g., "user_registration", "approval_process")
+ IS_DEFAULT BOOLEAN DEFAULT FALSE, -- Indicates if this is the default flow
+ UNIQUE (TENANT_ID, FLOW_NAME) -- Ensures a unique flow name per tenant
+);
+
+-- Create IDN_FLOW_NODE table with auto-incremental ID
+CREATE TABLE IDN_FLOW_NODE (
+ ID INT PRIMARY KEY AUTO_INCREMENT,
+ NODE_ID VARCHAR(50),
+ FLOW_ID VARCHAR(50),
+ NODE_TYPE VARCHAR(100),
+ IS_FIRST_NODE BOOLEAN DEFAULT FALSE,
+ UNIQUE (NODE_ID, FLOW_ID),
+ FOREIGN KEY (FLOW_ID) REFERENCES IDN_FLOW(ID) ON DELETE CASCADE -- Delete all nodes when a flow is deleted
+);
+
+-- Create IDN_FLOW_NODE_MAPPING table (to store multiple next nodes per node)
+CREATE TABLE IDN_FLOW_NODE_MAPPING (
+ ID INT PRIMARY KEY AUTO_INCREMENT, -- Unique auto-increment ID
+ FLOW_NODE_ID INT, -- References the current node
+ NEXT_NODE_ID INT, -- References the next node
+ TRIGGERING_ELEMENT VARCHAR(100), -- Defines the UI/event element triggering the transition
+ FOREIGN KEY (FLOW_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE, -- Delete mappings when node is deleted
+ FOREIGN KEY (NEXT_NODE_ID) REFERENCES IDN_FLOW_NODE(ID)
+);
+
+CREATE TABLE IDN_FLOW_PAGE (
+ ID INT PRIMARY KEY AUTO_INCREMENT,
+ FLOW_ID VARCHAR(50), -- References IDN_FLOW
+ FLOW_NODE_ID INT, -- References IDN_FLOW_NODE
+ STEP_ID VARCHAR(50), -- Stores step identifier for the page
+ PAGE_CONTENT BLOB,
+ TYPE VARCHAR(100), -- Page type (e.g., "FORM", "CONFIRMATION")
+ FOREIGN KEY (FLOW_ID) REFERENCES IDN_FLOW(ID) ON DELETE CASCADE,
+ FOREIGN KEY (FLOW_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE
+);
+
+-- Create IDN_FLOW_PAGE_META table (New)
+CREATE TABLE IDN_FLOW_PAGE_META (
+ ID INT PRIMARY KEY AUTO_INCREMENT,
+ PAGE_ID INT,
+ COORDINATE_X DOUBLE,
+ COORDINATE_Y DOUBLE,
+ HEIGHT DOUBLE,
+ WIDTH DOUBLE,
+ FOREIGN KEY (PAGE_ID) REFERENCES IDN_FLOW_PAGE(ID) ON DELETE CASCADE -- Delete metadata when page is deleted
+);
+
+-- Create IDN_FLOW_NODE_EXECUTOR table
+CREATE TABLE IDN_FLOW_NODE_EXECUTOR (
+ ID INT PRIMARY KEY AUTO_INCREMENT,
+ FLOW_NODE_ID INT, -- References IDN_FLOW_NODE
+ EXECUTOR_NAME VARCHAR(255),
+ IDP_NAME INT DEFAULT NULL, -- Default to NULL
+ FOREIGN KEY (FLOW_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE -- Delete executors when node is deleted
+);
+
-- --------------------------- INDEX CREATION -----------------------------
-- IDN_OAUTH2_ACCESS_TOKEN --
CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED);
diff --git a/features/registration-framework/org.wso2.carbon.identity.registration.framework.feature/pom.xml b/features/registration-framework/org.wso2.carbon.identity.registration.framework.feature/pom.xml
new file mode 100644
index 000000000000..7bf56a8429f3
--- /dev/null
+++ b/features/registration-framework/org.wso2.carbon.identity.registration.framework.feature/pom.xml
@@ -0,0 +1,71 @@
+
+
+
+
+ org.wso2.carbon.identity.framework
+ registration-framework-feature
+ 7.8.28-SNAPSHOT
+ ../pom.xml
+
+
+ 4.0.0
+ org.wso2.carbon.identity.registration.framework.feature
+ pom
+ Registration Framework Feature
+ http://wso2.org
+ This feature contains the bundles required for registration framework aggregated functionality
+
+
+
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.user.registration.mgt.server.feature
+ zip
+
+
+
+
+
+
+ org.wso2.maven
+ carbon-p2-plugin
+ ${carbon.p2.plugin.version}
+
+
+ 4-p2-feature-generation
+ package
+
+ p2-feature-gen
+
+
+ org.wso2.carbon.identity.registration.framework
+ ../../etc/feature.properties
+
+ org.wso2.carbon.identity.framework:org.wso2.carbon.identity.user.registration.mgt.server.feature
+
+
+
+
+
+
+
+
+
diff --git a/features/registration-framework/org.wso2.carbon.identity.user.registration.mgt.server.feature/pom.xml b/features/registration-framework/org.wso2.carbon.identity.user.registration.mgt.server.feature/pom.xml
new file mode 100644
index 000000000000..5936ffda6809
--- /dev/null
+++ b/features/registration-framework/org.wso2.carbon.identity.user.registration.mgt.server.feature/pom.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+ org.wso2.carbon.identity.framework
+ registration-framework-feature
+ 7.8.28-SNAPSHOT
+ ../pom.xml
+
+
+ 4.0.0
+ org.wso2.carbon.identity.user.registration.mgt.server.feature
+ pom
+ User Registration Management Feature
+ https://wso2.com
+ This feature contains the core bundles required for User Registration Management
+
+
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.user.registration.mgt
+
+
+
+
+
+
+ org.wso2.maven
+ carbon-p2-plugin
+ ${carbon.p2.plugin.version}
+
+
+ 4-p2-feature-generation
+ package
+
+ p2-feature-gen
+
+
+ org.wso2.carbon.identity.user.registration.mgt.server
+ ../../etc/feature.properties
+
+
+ org.wso2.carbon.p2.category.type:server
+
+
+
+
+ org.wso2.carbon.identity.framework:org.wso2.carbon.identity.user.registration.mgt
+
+
+
+
+
+
+
+
+
diff --git a/features/registration-framework/pom.xml b/features/registration-framework/pom.xml
new file mode 100644
index 000000000000..8e8f192f72ed
--- /dev/null
+++ b/features/registration-framework/pom.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+ org.wso2.carbon.identity.framework
+ identity-framework
+ 7.8.28-SNAPSHOT
+ ../../pom.xml
+
+
+ 4.0.0
+ registration-framework-feature
+ pom
+ WSO2 Carbon - Registration Framework Module
+ http://wso2.org
+
+
+ org.wso2.carbon.identity.user.registration.mgt.server.feature
+ org.wso2.carbon.identity.registration.framework.feature
+
+
+
diff --git a/pom.xml b/pom.xml
index 48d7c4628b9b..3e7bc24bf9ef 100644
--- a/pom.xml
+++ b/pom.xml
@@ -77,6 +77,7 @@
components/certificate-mgt
components/servlet-mgt
components/system-config-mgt
+ components/registration-framework
features/extension-mgt
components/consent-server-configs-mgt
features/security-mgt
@@ -116,6 +117,7 @@
features/certificate-mgt
features/servlet-mgt
features/system-config-mgt
+ features/registration-framework
@@ -755,6 +757,12 @@
org.wso2.carbon.identity.ai.service.mgt.server.feature
${project.version}
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.user.registration.mgt.server.feature
+ zip
+ ${project.version}
+
com.google.api-client
google-api-client
@@ -1777,6 +1785,11 @@
org.wso2.carbon.identity.ai.service.mgt
${project.version}
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.user.registration.mgt
+ ${project.version}
+
org.wso2.carbon.identity.framework
org.wso2.carbon.identity.servlet.mgt