From b7b36a475d640c929d27001beaf6570e7d2093d7 Mon Sep 17 00:00:00 2001 From: x032205 Date: Tue, 3 Jun 2025 20:12:29 -0400 Subject: [PATCH 1/8] fix invite bug --- frontend/src/pages/middlewares/restrict-login-signup.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/src/pages/middlewares/restrict-login-signup.tsx b/frontend/src/pages/middlewares/restrict-login-signup.tsx index f85ea44f2..f965769f8 100644 --- a/frontend/src/pages/middlewares/restrict-login-signup.tsx +++ b/frontend/src/pages/middlewares/restrict-login-signup.tsx @@ -90,6 +90,9 @@ export const Route = createFileRoute("/_restrict-login-signup")({ if (!data) return; setAuthToken(data.token); + + if (location.pathname === "/signupinvite") return; + // to do cli login if (search?.callback_port) { if (location.pathname.endsWith("select-organization") || location.pathname.endsWith("login")) From aa049dc43b4c83511e44f5f1b53d3d9289272373 Mon Sep 17 00:00:00 2001 From: x032205 Date: Tue, 3 Jun 2025 21:06:48 -0400 Subject: [PATCH 2/8] Fix invite problem on backend --- .../src/services/auth/auth-login-service.ts | 2 +- backend/src/services/org/org-dal.ts | 5 ++-- backend/src/services/org/org-service.ts | 4 +++- .../SignUpInvitePage/SignUpInvitePage.tsx | 23 +------------------ .../middlewares/restrict-login-signup.tsx | 4 ++-- 5 files changed, 10 insertions(+), 28 deletions(-) diff --git a/backend/src/services/auth/auth-login-service.ts b/backend/src/services/auth/auth-login-service.ts index bee85b14c..64ba573d5 100644 --- a/backend/src/services/auth/auth-login-service.ts +++ b/backend/src/services/auth/auth-login-service.ts @@ -397,7 +397,7 @@ export const authLoginServiceFactory = ({ // Check if the user actually has access to the specified organization. const userOrgs = await orgDAL.findAllOrgsByUserId(user.id); - const hasOrganizationMembership = userOrgs.some((org) => org.id === organizationId); + const hasOrganizationMembership = userOrgs.some((org) => org.id === organizationId && org.userStatus !== "invited"); const selectedOrg = await orgDAL.findById(organizationId); if (!hasOrganizationMembership) { diff --git a/backend/src/services/org/org-dal.ts b/backend/src/services/org/org-dal.ts index c4f0856a1..5730c0d92 100644 --- a/backend/src/services/org/org-dal.ts +++ b/backend/src/services/org/org-dal.ts @@ -212,7 +212,7 @@ export const orgDALFactory = (db: TDbClient) => { // special query const findAllOrgsByUserId = async ( userId: string - ): Promise<(TOrganizations & { orgAuthMethod: string; userRole: string })[]> => { + ): Promise<(TOrganizations & { orgAuthMethod: string; userRole: string; userStatus: string })[]> => { try { const org = (await db .replicaNode()(TableName.OrgMembership) @@ -234,6 +234,7 @@ export const orgDALFactory = (db: TDbClient) => { }) .select(selectAllTableCols(TableName.Organization)) .select(db.ref("role").withSchema(TableName.OrgMembership).as("userRole")) + .select(db.ref("status").withSchema(TableName.OrgMembership).as("userStatus")) .select( db.raw(` CASE @@ -242,7 +243,7 @@ export const orgDALFactory = (db: TDbClient) => { ELSE '' END as "orgAuthMethod" `) - )) as (TOrganizations & { orgAuthMethod: string; userRole: string })[]; + )) as (TOrganizations & { orgAuthMethod: string; userRole: string; userStatus: string })[]; return org; } catch (error) { diff --git a/backend/src/services/org/org-service.ts b/backend/src/services/org/org-service.ts index bfd24e639..6e8766a84 100644 --- a/backend/src/services/org/org-service.ts +++ b/backend/src/services/org/org-service.ts @@ -183,7 +183,9 @@ export const orgServiceFactory = ({ * */ const findAllOrganizationOfUser = async (userId: string) => { const orgs = await orgDAL.findAllOrgsByUserId(userId); - return orgs; + + // Filter out orgs where the membership object is an invitation + return orgs.filter((org) => org.userStatus !== "invited"); }; /* * Get all workspace members diff --git a/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx b/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx index a01545537..f9dd81f5d 100644 --- a/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx +++ b/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx @@ -243,28 +243,7 @@ export const SignupInvitePage = () => { SecurityClient.setSignupToken(response.token); setStep(2); } else { - const redirectExistingUser = async () => { - try { - const { token: mfaToken, isMfaEnabled } = await selectOrganization({ - organizationId - }); - - if (isMfaEnabled) { - SecurityClient.setMfaToken(mfaToken); - toggleShowMfa.on(); - setMfaSuccessCallback(() => redirectExistingUser); - return; - } - - // user will be redirected to dashboard - // if not logged in gets kicked out to login - await navigateUserToOrg(navigate, organizationId); - } catch (err) { - navigate({ to: "/login" }); - } - }; - - await redirectExistingUser(); + navigate({ to: "/login" }); } } } catch (err) { diff --git a/frontend/src/pages/middlewares/restrict-login-signup.tsx b/frontend/src/pages/middlewares/restrict-login-signup.tsx index f965769f8..31434458f 100644 --- a/frontend/src/pages/middlewares/restrict-login-signup.tsx +++ b/frontend/src/pages/middlewares/restrict-login-signup.tsx @@ -74,6 +74,8 @@ export const Route = createFileRoute("/_restrict-login-signup")({ middlewares: [stripSearchParams({ callback_port: undefined })] }, beforeLoad: async ({ context, location, search }) => { + if (location.pathname === "/signupinvite") return; + if (!context.serverConfig.initialized) { if (location.pathname.endsWith("/admin/signup")) return; throw redirect({ to: "/admin/signup" }); @@ -91,8 +93,6 @@ export const Route = createFileRoute("/_restrict-login-signup")({ setAuthToken(data.token); - if (location.pathname === "/signupinvite") return; - // to do cli login if (search?.callback_port) { if (location.pathname.endsWith("select-organization") || location.pathname.endsWith("login")) From e2680d9aeef3b9e5f60fbf689f62afc2ddecd532 Mon Sep 17 00:00:00 2001 From: x032205 Date: Tue, 3 Jun 2025 21:48:42 -0400 Subject: [PATCH 3/8] Insert old code as comment --- .../SignUpInvitePage/SignUpInvitePage.tsx | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx b/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx index f9dd81f5d..39eb8d686 100644 --- a/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx +++ b/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx @@ -244,6 +244,30 @@ export const SignupInvitePage = () => { setStep(2); } else { navigate({ to: "/login" }); + + // TODO: Redirect user to the newly joined org. The commented code does not work due to auth issues. + // const redirectExistingUser = async () => { + // try { + // const { token: mfaToken, isMfaEnabled } = await selectOrganization({ + // organizationId + // }); + + // if (isMfaEnabled) { + // SecurityClient.setMfaToken(mfaToken); + // toggleShowMfa.on(); + // setMfaSuccessCallback(() => redirectExistingUser); + // return; + // } + + // // user will be redirected to dashboard + // // if not logged in gets kicked out to login + // await navigateUserToOrg(navigate, organizationId); + // } catch (err) { + // navigate({ to: "/login" }); + // } + // }; + + // await redirectExistingUser(); } } } catch (err) { From 92b9abb52b699710d42fc9b76aba061dda06ef36 Mon Sep 17 00:00:00 2001 From: x032205 Date: Tue, 3 Jun 2025 21:48:59 -0400 Subject: [PATCH 4/8] Fix type issue --- frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx b/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx index 39eb8d686..19a2694f8 100644 --- a/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx +++ b/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx @@ -29,7 +29,7 @@ import { MfaMethod } from "@app/hooks/api/auth/types"; import { fetchOrganizations } from "@app/hooks/api/organization/queries"; import { ProjectType } from "@app/hooks/api/workspace/types"; -import { navigateUserToOrg } from "../LoginPage/Login.utils"; +// import { navigateUserToOrg } from "../LoginPage/Login.utils"; // eslint-disable-next-line new-cap const client = new jsrp.client(); From 952e60f08afac6a946c4e60072f80629a15d239e Mon Sep 17 00:00:00 2001 From: x032205 Date: Wed, 4 Jun 2025 16:54:14 -0400 Subject: [PATCH 5/8] Select organization checkpoint --- frontend/src/pages/auth/SelectOrgPage/route.tsx | 3 ++- .../src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx | 2 +- frontend/src/pages/middlewares/restrict-login-signup.tsx | 7 +++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/frontend/src/pages/auth/SelectOrgPage/route.tsx b/frontend/src/pages/auth/SelectOrgPage/route.tsx index 445479b3f..27ad4bb94 100644 --- a/frontend/src/pages/auth/SelectOrgPage/route.tsx +++ b/frontend/src/pages/auth/SelectOrgPage/route.tsx @@ -7,7 +7,8 @@ import { SelectOrganizationPage } from "./SelectOrgPage"; export const SelectOrganizationPageQueryParams = z.object({ org_id: z.string().optional().catch(""), callback_port: z.coerce.number().optional().catch(undefined), - is_admin_login: z.boolean().optional().catch(false) + is_admin_login: z.boolean().optional().catch(false), + force: z.boolean().optional() }); export const Route = createFileRoute("/_restrict-login-signup/login/select-organization")({ diff --git a/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx b/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx index 19a2694f8..2db4f7901 100644 --- a/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx +++ b/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx @@ -243,7 +243,7 @@ export const SignupInvitePage = () => { SecurityClient.setSignupToken(response.token); setStep(2); } else { - navigate({ to: "/login" }); + navigate({ to: "/login/select-organization", search: { force: true } }); // TODO: Redirect user to the newly joined org. The commented code does not work due to auth issues. // const redirectExistingUser = async () => { diff --git a/frontend/src/pages/middlewares/restrict-login-signup.tsx b/frontend/src/pages/middlewares/restrict-login-signup.tsx index 31434458f..210f848cc 100644 --- a/frontend/src/pages/middlewares/restrict-login-signup.tsx +++ b/frontend/src/pages/middlewares/restrict-login-signup.tsx @@ -15,7 +15,8 @@ import { setAuthToken } from "@app/hooks/api/reactQuery"; import { ProjectType } from "@app/hooks/api/workspace/types"; const QueryParamsSchema = z.object({ - callback_port: z.coerce.number().optional().catch(undefined) + callback_port: z.coerce.number().optional().catch(undefined), + force: z.boolean().optional() }); export const AuthConsentWrapper = () => { @@ -71,7 +72,7 @@ export const AuthConsentWrapper = () => { export const Route = createFileRoute("/_restrict-login-signup")({ validateSearch: zodValidator(QueryParamsSchema), search: { - middlewares: [stripSearchParams({ callback_port: undefined })] + middlewares: [stripSearchParams({ callback_port: undefined, force: undefined })] }, beforeLoad: async ({ context, location, search }) => { if (location.pathname === "/signupinvite") return; @@ -93,6 +94,8 @@ export const Route = createFileRoute("/_restrict-login-signup")({ setAuthToken(data.token); + if (location.pathname.endsWith("select-organization") && search?.force === true) return; + // to do cli login if (search?.callback_port) { if (location.pathname.endsWith("select-organization") || location.pathname.endsWith("login")) From 5255f0ac17a35a0ed9d1bc4dff03891fff226a11 Mon Sep 17 00:00:00 2001 From: x032205 Date: Thu, 5 Jun 2025 11:30:05 -0400 Subject: [PATCH 6/8] Fix select org --- .../src/services/auth/auth-login-service.ts | 2 +- .../auth/SelectOrgPage/SelectOrgSection.tsx | 5 +++- .../SignUpInvitePage/SignUpInvitePage.tsx | 24 ------------------- .../middlewares/restrict-login-signup.tsx | 1 + 4 files changed, 6 insertions(+), 26 deletions(-) diff --git a/backend/src/services/auth/auth-login-service.ts b/backend/src/services/auth/auth-login-service.ts index 64ba573d5..bee85b14c 100644 --- a/backend/src/services/auth/auth-login-service.ts +++ b/backend/src/services/auth/auth-login-service.ts @@ -397,7 +397,7 @@ export const authLoginServiceFactory = ({ // Check if the user actually has access to the specified organization. const userOrgs = await orgDAL.findAllOrgsByUserId(user.id); - const hasOrganizationMembership = userOrgs.some((org) => org.id === organizationId && org.userStatus !== "invited"); + const hasOrganizationMembership = userOrgs.some((org) => org.id === organizationId); const selectedOrg = await orgDAL.findById(organizationId); if (!hasOrganizationMembership) { diff --git a/frontend/src/pages/auth/SelectOrgPage/SelectOrgSection.tsx b/frontend/src/pages/auth/SelectOrgPage/SelectOrgSection.tsx index a736b52a7..ac194cd05 100644 --- a/frontend/src/pages/auth/SelectOrgPage/SelectOrgSection.tsx +++ b/frontend/src/pages/auth/SelectOrgPage/SelectOrgSection.tsx @@ -3,7 +3,7 @@ import { Helmet } from "react-helmet"; import { useTranslation } from "react-i18next"; import { faArrowRight } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { Link, useNavigate } from "@tanstack/react-router"; +import { Link, useNavigate, useRouter } from "@tanstack/react-router"; import axios from "axios"; import { addSeconds, formatISO } from "date-fns"; import { jwtDecode } from "jwt-decode"; @@ -51,6 +51,7 @@ export const SelectOrganizationSection = () => { const [mfaSuccessCallback, setMfaSuccessCallback] = useState<() => void>(() => {}); + const router = useRouter(); const queryParams = new URLSearchParams(window.location.search); const orgId = queryParams.get("org_id"); const callbackPort = queryParams.get("callback_port"); @@ -118,6 +119,8 @@ export const SelectOrganizationSection = () => { }) .finally(() => setIsInitialOrgCheckLoading(false)); + await router.invalidate(); + if (isMfaEnabled) { SecurityClient.setMfaToken(token); if (mfaMethod) { diff --git a/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx b/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx index 2db4f7901..23a9c4930 100644 --- a/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx +++ b/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx @@ -244,30 +244,6 @@ export const SignupInvitePage = () => { setStep(2); } else { navigate({ to: "/login/select-organization", search: { force: true } }); - - // TODO: Redirect user to the newly joined org. The commented code does not work due to auth issues. - // const redirectExistingUser = async () => { - // try { - // const { token: mfaToken, isMfaEnabled } = await selectOrganization({ - // organizationId - // }); - - // if (isMfaEnabled) { - // SecurityClient.setMfaToken(mfaToken); - // toggleShowMfa.on(); - // setMfaSuccessCallback(() => redirectExistingUser); - // return; - // } - - // // user will be redirected to dashboard - // // if not logged in gets kicked out to login - // await navigateUserToOrg(navigate, organizationId); - // } catch (err) { - // navigate({ to: "/login" }); - // } - // }; - - // await redirectExistingUser(); } } } catch (err) { diff --git a/frontend/src/pages/middlewares/restrict-login-signup.tsx b/frontend/src/pages/middlewares/restrict-login-signup.tsx index 210f848cc..f22ba82fe 100644 --- a/frontend/src/pages/middlewares/restrict-login-signup.tsx +++ b/frontend/src/pages/middlewares/restrict-login-signup.tsx @@ -94,6 +94,7 @@ export const Route = createFileRoute("/_restrict-login-signup")({ setAuthToken(data.token); + // Avoid redirect if on select-organization page with force=true if (location.pathname.endsWith("select-organization") && search?.force === true) return; // to do cli login From d2b0ca94d840ce47fc25acd8748360a75ef9735c Mon Sep 17 00:00:00 2001 From: x032205 Date: Thu, 5 Jun 2025 11:59:10 -0400 Subject: [PATCH 7/8] Remove commented line --- frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx b/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx index 23a9c4930..89aa227c7 100644 --- a/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx +++ b/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx @@ -29,8 +29,6 @@ import { MfaMethod } from "@app/hooks/api/auth/types"; import { fetchOrganizations } from "@app/hooks/api/organization/queries"; import { ProjectType } from "@app/hooks/api/workspace/types"; -// import { navigateUserToOrg } from "../LoginPage/Login.utils"; - // eslint-disable-next-line new-cap const client = new jsrp.client(); From b394e191a8a36949789deac3a3104d8a055fc023 Mon Sep 17 00:00:00 2001 From: x032205 Date: Fri, 6 Jun 2025 13:02:23 -0400 Subject: [PATCH 8/8] Fix accepting invite while logged out --- backend/src/services/auth/auth-login-service.ts | 2 +- .../src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx | 7 ++++++- frontend/src/pages/middlewares/restrict-login-signup.tsx | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/backend/src/services/auth/auth-login-service.ts b/backend/src/services/auth/auth-login-service.ts index bee85b14c..64ba573d5 100644 --- a/backend/src/services/auth/auth-login-service.ts +++ b/backend/src/services/auth/auth-login-service.ts @@ -397,7 +397,7 @@ export const authLoginServiceFactory = ({ // Check if the user actually has access to the specified organization. const userOrgs = await orgDAL.findAllOrgsByUserId(user.id); - const hasOrganizationMembership = userOrgs.some((org) => org.id === organizationId); + const hasOrganizationMembership = userOrgs.some((org) => org.id === organizationId && org.userStatus !== "invited"); const selectedOrg = await orgDAL.findById(organizationId); if (!hasOrganizationMembership) { diff --git a/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx b/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx index 89aa227c7..9cb739736 100644 --- a/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx +++ b/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx @@ -28,6 +28,7 @@ import { import { MfaMethod } from "@app/hooks/api/auth/types"; import { fetchOrganizations } from "@app/hooks/api/organization/queries"; import { ProjectType } from "@app/hooks/api/workspace/types"; +import { isLoggedIn } from "@app/hooks/api/reactQuery"; // eslint-disable-next-line new-cap const client = new jsrp.client(); @@ -69,6 +70,8 @@ export const SignupInvitePage = () => { const { mutateAsync: selectOrganization } = useSelectOrganization(); + const loggedIn = isLoggedIn(); + // Verifies if the information that the users entered (name, workspace) is there, and if the password matched the criteria. const signupErrorCheck = async () => { setIsLoading(true); @@ -240,8 +243,10 @@ export const SignupInvitePage = () => { if (response?.token) { SecurityClient.setSignupToken(response.token); setStep(2); - } else { + } else if (loggedIn) { navigate({ to: "/login/select-organization", search: { force: true } }); + } else { + navigate({ to: "/login" }); } } } catch (err) { diff --git a/frontend/src/pages/middlewares/restrict-login-signup.tsx b/frontend/src/pages/middlewares/restrict-login-signup.tsx index f22ba82fe..73964868a 100644 --- a/frontend/src/pages/middlewares/restrict-login-signup.tsx +++ b/frontend/src/pages/middlewares/restrict-login-signup.tsx @@ -75,8 +75,6 @@ export const Route = createFileRoute("/_restrict-login-signup")({ middlewares: [stripSearchParams({ callback_port: undefined, force: undefined })] }, beforeLoad: async ({ context, location, search }) => { - if (location.pathname === "/signupinvite") return; - if (!context.serverConfig.initialized) { if (location.pathname.endsWith("/admin/signup")) return; throw redirect({ to: "/admin/signup" }); @@ -94,6 +92,8 @@ export const Route = createFileRoute("/_restrict-login-signup")({ setAuthToken(data.token); + if (location.pathname === "/signupinvite") return; + // Avoid redirect if on select-organization page with force=true if (location.pathname.endsWith("select-organization") && search?.force === true) return;