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 4a72f12e8..63cb8935d 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/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/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 a01545537..9cb739736 100644 --- a/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx +++ b/frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx @@ -28,8 +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 { navigateUserToOrg } from "../LoginPage/Login.utils"; +import { isLoggedIn } from "@app/hooks/api/reactQuery"; // eslint-disable-next-line new-cap const client = new jsrp.client(); @@ -71,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); @@ -242,29 +243,10 @@ export const SignupInvitePage = () => { if (response?.token) { SecurityClient.setSignupToken(response.token); setStep(2); + } else if (loggedIn) { + navigate({ to: "/login/select-organization", search: { force: true } }); } 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 f85ea44f2..73964868a 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 (!context.serverConfig.initialized) { @@ -90,6 +91,12 @@ export const Route = createFileRoute("/_restrict-login-signup")({ if (!data) return; 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; + // to do cli login if (search?.callback_port) { if (location.pathname.endsWith("select-organization") || location.pathname.endsWith("login"))