From 4b58461e23d7d51f1d8c333926297ebe2fb85b84 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Thu, 17 Apr 2025 16:10:13 +0500 Subject: [PATCH 1/3] show error message if user not found in login flow --- client/packages/lowcoder/src/i18n/locales/en.ts | 1 + .../lowcoder/src/pages/userAuth/formLoginSteps.tsx | 13 +++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts index 735b797d4e..ce47ff7093 100644 --- a/client/packages/lowcoder/src/i18n/locales/en.ts +++ b/client/packages/lowcoder/src/i18n/locales/en.ts @@ -3205,6 +3205,7 @@ export const en = { "enterPassword": "Enter your password", "selectAuthProvider": "Select Authentication Provider", "selectWorkspace": "Select your workspace", + "userNotFound": "User not found. Please make sure you entered the correct email." }, "preLoad": { "jsLibraryHelpText": "Add JavaScript Libraries to Your Current Application via URL Addresses. lodash, day.js, uuid, numbro are Built into the System for Immediate Use. JavaScript Libraries are Loaded Before the Application is Initialized, Which Can Have an Impact on Application Performance.", diff --git a/client/packages/lowcoder/src/pages/userAuth/formLoginSteps.tsx b/client/packages/lowcoder/src/pages/userAuth/formLoginSteps.tsx index aced446bd9..a04b9b34c9 100644 --- a/client/packages/lowcoder/src/pages/userAuth/formLoginSteps.tsx +++ b/client/packages/lowcoder/src/pages/userAuth/formLoginSteps.tsx @@ -177,11 +177,12 @@ export default function FormLoginSteps(props: FormLoginProps) { setElements({elements: resp.data || [], total: resp.total || 1}) setOrgList(resp.data); if (!resp.data.length) { - history.push( - AUTH_REGISTER_URL, - {...location.state || {}, email: account}, - ) - return; + // history.push( + // AUTH_REGISTER_URL, + // {...location.state || {}, email: account}, + // ) + // return; + throw new Error(trans("userAuth.userNotFound")); } if (resp.data.length === 1) { setOrganizationId(resp.data[0].orgId); @@ -228,7 +229,7 @@ export default function FormLoginSteps(props: FormLoginProps) { {trans("userAuth.register")} From a43628bb8cd31c9b67efe3b8ed55017c699c4d09 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Fri, 18 Apr 2025 02:11:50 +0500 Subject: [PATCH 2/3] update login flow for enterprise mode --- .../src/pages/userAuth/formLoginSteps.tsx | 82 +++++++++++++++---- .../lowcoder/src/pages/userAuth/register.tsx | 8 +- .../userAuth/thirdParty/thirdPartyAuth.tsx | 17 +++- 3 files changed, 85 insertions(+), 22 deletions(-) diff --git a/client/packages/lowcoder/src/pages/userAuth/formLoginSteps.tsx b/client/packages/lowcoder/src/pages/userAuth/formLoginSteps.tsx index a04b9b34c9..b04e4b8367 100644 --- a/client/packages/lowcoder/src/pages/userAuth/formLoginSteps.tsx +++ b/client/packages/lowcoder/src/pages/userAuth/formLoginSteps.tsx @@ -4,7 +4,7 @@ import { ConfirmButton, StyledRouteLink, } from "pages/userAuth/authComponents"; -import React, { useContext, useEffect, useState } from "react"; +import React, { useContext, useEffect, useMemo, useState } from "react"; import styled from "styled-components"; import UserApi from "api/userApi"; import { useRedirectUrl } from "util/hooks"; @@ -19,7 +19,7 @@ import { Divider } from "antd"; import Flex from "antd/es/flex"; import { validateResponse } from "@lowcoder-ee/api/apiUtils"; import OrgApi from "@lowcoder-ee/api/orgApi"; -import { AccountLoginWrapper } from "./formLoginAdmin"; +import FormLogin, { AccountLoginWrapper } from "./formLoginAdmin"; import { default as Button } from "antd/es/button"; import LeftOutlined from "@ant-design/icons/LeftOutlined"; import { fetchConfigAction } from "@lowcoder-ee/redux/reduxActions/configActions"; @@ -28,6 +28,9 @@ import history from "util/history"; import { getServerSettings } from "@lowcoder-ee/redux/selectors/applicationSelector"; import {fetchOrgPaginationByEmail} from "@lowcoder-ee/util/pagination/axios"; import PaginationComp from "@lowcoder-ee/util/pagination/Pagination"; +import { getSystemConfigFetching } from "@lowcoder-ee/redux/selectors/configSelectors"; +import Spin from "antd/es/spin"; +import LoadingOutlined from "@ant-design/icons/LoadingOutlined"; const StyledCard = styled.div<{$selected: boolean}>` display: flex; @@ -107,18 +110,28 @@ export default function FormLoginSteps(props: FormLoginProps) { const { systemConfig, inviteInfo, fetchUserAfterAuthSuccess } = useContext(AuthContext); const invitationId = inviteInfo?.invitationId; const authId = systemConfig?.form.id; - const isFormLoginEnabled = systemConfig?.form.enableLogin; + const isFormLoginEnabled = systemConfig?.form.enableLogin; // check from configs const [orgLoading, setOrgLoading] = useState(false); const [orgList, setOrgList] = useState([]); const [currentStep, setCurrentStep] = useState(CurrentStepEnum.EMAIL); const [organizationId, setOrganizationId] = useState(props.organizationId); const [skipWorkspaceStep, setSkipWorkspaceStep] = useState(false); const [signupEnabled, setSignupEnabled] = useState(true); + const [signinEnabled, setSigninEnabled] = useState(true); // check from server settings const serverSettings = useSelector(getServerSettings); + const isFetchingConfig = useSelector(getSystemConfigFetching); const [elements, setElements] = useState({ elements: [], total: 0 }); const [currentPage, setCurrentPage] = useState(1); const [pageSize, setPageSize] = useState(10); + const isEmailLoginEnabled = useMemo(() => { + return isFormLoginEnabled && signinEnabled; + }, [isFormLoginEnabled, signinEnabled]); + + const isEnterpriseMode = useMemo(() => { + return serverSettings?.LOWCODER_WORKSPACE_MODE === "ENTERPRISE" || serverSettings?.LOWCODER_WORKSPACE_MODE === "SINGLEWORKSPACE"; + }, [serverSettings]); + useEffect(() => { if (account) fetchOrgPaginationByEmail({ @@ -133,11 +146,13 @@ export default function FormLoginSteps(props: FormLoginProps) { }, [pageSize, currentPage]) useEffect(() => { - const { LOWCODER_EMAIL_SIGNUP_ENABLED } = serverSettings; - if (!LOWCODER_EMAIL_SIGNUP_ENABLED) { - return setSignupEnabled(true); - } + const { + LOWCODER_EMAIL_SIGNUP_ENABLED, + LOWCODER_EMAIL_AUTH_ENABLED, + } = serverSettings; + setSignupEnabled(LOWCODER_EMAIL_SIGNUP_ENABLED === 'true'); + setSigninEnabled(LOWCODER_EMAIL_AUTH_ENABLED === 'true'); }, [serverSettings]); const { onSubmit, loading } = useAuthSubmit( @@ -167,8 +182,9 @@ export default function FormLoginSteps(props: FormLoginProps) { } setOrgLoading(true); + // for enterprise mode, we will not ask for email in first step fetchOrgPaginationByEmail({ - email: account, + email: isEnterpriseMode ? ' ' : account, pageNum: currentPage, pageSize: pageSize }) @@ -177,16 +193,13 @@ export default function FormLoginSteps(props: FormLoginProps) { setElements({elements: resp.data || [], total: resp.total || 1}) setOrgList(resp.data); if (!resp.data.length) { - // history.push( - // AUTH_REGISTER_URL, - // {...location.state || {}, email: account}, - // ) - // return; throw new Error(trans("userAuth.userNotFound")); } if (resp.data.length === 1) { - setOrganizationId(resp.data[0].orgId); - dispatch(fetchConfigAction(resp.data[0].orgId)); + // in Enterprise mode, we will get org data in different format + const selectedOrgId = isEnterpriseMode ? resp.data[0].id : resp.data[0].orgId; + setOrganizationId(selectedOrgId); + dispatch(fetchConfigAction(selectedOrgId)); setCurrentStep(CurrentStepEnum.AUTH_PROVIDERS); return; } @@ -203,6 +216,39 @@ export default function FormLoginSteps(props: FormLoginProps) { }); } + useEffect(() => { + if (isEnterpriseMode) { + // dispatch(fetchConfigAction()); + fetchOrgsByEmail(); + } + }, [isEnterpriseMode]); + + if (isEnterpriseMode) { + return ( + } spinning={isFetchingConfig}> + { isEmailLoginEnabled && } + + {signupEnabled && ( + <> + + + + {trans("userAuth.register")} + + + + )} + + ); + } + if(currentStep === CurrentStepEnum.EMAIL) { return ( <> @@ -281,10 +327,10 @@ export default function FormLoginSteps(props: FormLoginProps) { }} /> - {isFormLoginEnabled && ( + {isEmailLoginEnabled && ( <> )} - {isFormLoginEnabled && signupEnabled && ( + {isEmailLoginEnabled && signupEnabled && ( <> diff --git a/client/packages/lowcoder/src/pages/userAuth/register.tsx b/client/packages/lowcoder/src/pages/userAuth/register.tsx index 62bd7f7c2c..0280947eb2 100644 --- a/client/packages/lowcoder/src/pages/userAuth/register.tsx +++ b/client/packages/lowcoder/src/pages/userAuth/register.tsx @@ -63,12 +63,16 @@ function UserRegister() { return inviteInfo?.invitedOrganizationId; } return orgId; - }, [ inviteInfo, orgId ]) + }, [ inviteInfo, orgId ]); const authId = systemConfig?.form.id; const serverSettings = useSelector(getServerSettings); + const isEnterpriseMode = useMemo(() => { + return serverSettings?.LOWCODER_WORKSPACE_MODE === "ENTERPRISE" || serverSettings?.LOWCODER_WORKSPACE_MODE === "SINGLEWORKSPACE"; + }, [serverSettings]); + useEffect(() => { const { LOWCODER_EMAIL_SIGNUP_ENABLED } = serverSettings; if( @@ -160,7 +164,7 @@ function UserRegister() { {trans("userAuth.register")} setSubmitBtnDisable(!e.target.checked)} /> - {organizationId && ( + {(organizationId || isEnterpriseMode) && ( { + return isFormLoginEnabled && serverSettings.LOWCODER_EMAIL_AUTH_ENABLED === 'true'; + }, [isFormLoginEnabled, serverSettings]); + + const isEmailSignupEnabled = useMemo(() => { + return serverSettings.LOWCODER_EMAIL_SIGNUP_ENABLED === 'true'; + }, [serverSettings]); if (systemConfigFetching) { return } />; @@ -140,7 +150,10 @@ export function ThirdPartyAuth(props: { }); return ( - { isFormLoginEnabled && Boolean(socialLoginButtons.length) && ( + { ( + (isEmailLoginEnabled && props.authGoal === 'login') + || (isEmailSignupEnabled && props.authGoal === 'register') + ) && Boolean(socialLoginButtons.length) && ( or From 79c07b0af58d1b636707c7b496347e4aa34c2790 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Sat, 19 Apr 2025 01:22:04 +0500 Subject: [PATCH 3/3] on logout, user should redirect to workspace login page when login/register using workspace url --- client/packages/lowcoder/src/api/apiUtils.ts | 9 ++++++++- .../lowcoder/src/pages/common/profileDropdown.tsx | 8 +++++++- .../lowcoder/src/pages/userAuth/formLoginSteps.tsx | 11 +++++++++-- .../packages/lowcoder/src/pages/userAuth/register.tsx | 9 ++++++++- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/client/packages/lowcoder/src/api/apiUtils.ts b/client/packages/lowcoder/src/api/apiUtils.ts index 8396e35014..3b8e4e4344 100644 --- a/client/packages/lowcoder/src/api/apiUtils.ts +++ b/client/packages/lowcoder/src/api/apiUtils.ts @@ -122,7 +122,14 @@ export const apiFailureResponseInterceptor = (error: any) => { if (!notAuthRequiredPath(error.config?.url)) { if (error.response.status === API_STATUS_CODES.REQUEST_NOT_AUTHORISED) { // get x-org-id from failed request - const organizationId = error.response.headers['x-org-id'] || undefined; + let organizationId; + if (error.response.headers['x-org-id']) { + organizationId = error.response.headers['x-org-id']; + } + if (localStorage.getItem('lowcoder_login_orgId')) { + organizationId = localStorage.getItem('lowcoder_login_orgId'); + localStorage.removeItem('lowcoder_login_orgId'); + } // Redirect to login and set a redirect url. StoreRegistry.getStore().dispatch( logoutAction({ diff --git a/client/packages/lowcoder/src/pages/common/profileDropdown.tsx b/client/packages/lowcoder/src/pages/common/profileDropdown.tsx index 992227bd1d..d3f8b2a6d9 100644 --- a/client/packages/lowcoder/src/pages/common/profileDropdown.tsx +++ b/client/packages/lowcoder/src/pages/common/profileDropdown.tsx @@ -150,7 +150,13 @@ export default function ProfileDropdown(props: DropDownProps) { dispatch(profileSettingModalVisible(true)); } else if (e.key === "logout") { // logout - dispatch(logoutAction({})); + const organizationId = localStorage.getItem('lowcoder_login_orgId'); + if (organizationId) { + localStorage.removeItem('lowcoder_login_orgId'); + } + dispatch(logoutAction({ + organizationId: organizationId || undefined, + })); } else if (e.keyPath.includes("switchOrg")) { if (e.key === "newOrganization") { // create new organization diff --git a/client/packages/lowcoder/src/pages/userAuth/formLoginSteps.tsx b/client/packages/lowcoder/src/pages/userAuth/formLoginSteps.tsx index b04e4b8367..1e036f3a32 100644 --- a/client/packages/lowcoder/src/pages/userAuth/formLoginSteps.tsx +++ b/client/packages/lowcoder/src/pages/userAuth/formLoginSteps.tsx @@ -155,6 +155,13 @@ export default function FormLoginSteps(props: FormLoginProps) { setSigninEnabled(LOWCODER_EMAIL_AUTH_ENABLED === 'true'); }, [serverSettings]); + const afterLoginSuccess = () => { + if (props.organizationId) { + localStorage.setItem("lowcoder_login_orgId", props.organizationId); + } + fetchUserAfterAuthSuccess?.(); + } + const { onSubmit, loading } = useAuthSubmit( () => UserApi.formLogin({ @@ -168,7 +175,7 @@ export default function FormLoginSteps(props: FormLoginProps) { }), false, redirectUrl, - fetchUserAfterAuthSuccess, + afterLoginSuccess, ); const fetchOrgsByEmail = () => { @@ -274,7 +281,7 @@ export default function FormLoginSteps(props: FormLoginProps) { {trans("userAuth.register")} diff --git a/client/packages/lowcoder/src/pages/userAuth/register.tsx b/client/packages/lowcoder/src/pages/userAuth/register.tsx index 0280947eb2..41ba0115ef 100644 --- a/client/packages/lowcoder/src/pages/userAuth/register.tsx +++ b/client/packages/lowcoder/src/pages/userAuth/register.tsx @@ -86,6 +86,13 @@ function UserRegister() { }; }, [serverSettings]); + const afterLoginSuccess = () => { + if (organizationId) { + localStorage.setItem("lowcoder_login_orgId", organizationId); + } + fetchUserAfterAuthSuccess?.(); + } + const { loading, onSubmit } = useAuthSubmit( () => UserApi.formLogin({ @@ -99,7 +106,7 @@ function UserRegister() { }), false, redirectUrl, - fetchUserAfterAuthSuccess, + afterLoginSuccess, ); const checkEmailExist = () => {