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 735b797d4..ce47ff709 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 aced446bd..a04b9b34c 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 a04b9b34c..b04e4b836 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 62bd7f7c2..0280947eb 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 8396e3501..3b8e4e434 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 992227bd1..d3f8b2a6d 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 b04e4b836..1e036f3a3 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 0280947eb..41ba0115e 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 = () => {