diff --git a/backend/src/services/auth/auth-login-service.ts b/backend/src/services/auth/auth-login-service.ts index 39f3162d1..83da4724e 100644 --- a/backend/src/services/auth/auth-login-service.ts +++ b/backend/src/services/auth/auth-login-service.ts @@ -354,7 +354,7 @@ export const authLoginServiceFactory = ({ } // send multi factor auth token if they it enabled - if (user.isMfaEnabled && user.email && !decodedToken.isMfaVerified) { + if ((selectedOrg.enforceMfa || user.isMfaEnabled) && user.email && !decodedToken.isMfaVerified) { enforceUserLockStatus(Boolean(user.isLocked), user.temporaryLockDateEnd); const mfaToken = jwt.sign( diff --git a/frontend/src/hooks/api/auth/queries.tsx b/frontend/src/hooks/api/auth/queries.tsx index 91c2e7be1..dce930418 100644 --- a/frontend/src/hooks/api/auth/queries.tsx +++ b/frontend/src/hooks/api/auth/queries.tsx @@ -79,7 +79,7 @@ export const useSelectOrganization = () => { const data = await selectOrganization(details); // If a custom user agent is set, then this session is meant for another consuming application, not the web application. - if (!details.userAgent) { + if (!details.userAgent && !data.isMfaEnabled) { SecurityClient.setToken(data.token); SecurityClient.setProviderAuthToken(""); } diff --git a/frontend/src/layouts/AppLayout/AppLayout.tsx b/frontend/src/layouts/AppLayout/AppLayout.tsx index 6e5c18ea0..f0737c32d 100644 --- a/frontend/src/layouts/AppLayout/AppLayout.tsx +++ b/frontend/src/layouts/AppLayout/AppLayout.tsx @@ -5,7 +5,7 @@ /* eslint-disable no-var */ /* eslint-disable func-names */ -import { useEffect, useMemo } from "react"; +import { useEffect, useMemo, useState } from "react"; import { Controller, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import Link from "next/link"; @@ -35,6 +35,7 @@ import * as yup from "yup"; import { createNotification } from "@app/components/notifications"; import { OrgPermissionCan } from "@app/components/permissions"; import { tempLocalStorage } from "@app/components/utilities/checks/tempLocalStorage"; +import SecurityClient from "@app/components/utilities/SecurityClient"; import { Accordion, AccordionContent, @@ -64,7 +65,7 @@ import { useUser, useWorkspace } from "@app/context"; -import { usePopUp } from "@app/hooks"; +import { usePopUp, useToggle } from "@app/hooks"; import { fetchOrgUsers, useAddUserToWsNonE2EE, @@ -82,6 +83,7 @@ import { useUpdateUserProjectFavorites } from "@app/hooks/api/users/mutation"; import { useGetUserProjectFavorites } from "@app/hooks/api/users/queries"; import { AuthMethod } from "@app/hooks/api/users/types"; import { navigateUserToOrg } from "@app/views/Login/Login.utils"; +import { Mfa } from "@app/views/Login/Mfa"; import { CreateOrgModal } from "@app/views/Org/components"; import { WishForm } from "./components/WishForm/WishForm"; @@ -136,6 +138,8 @@ export const AppLayout = ({ children }: LayoutProps) => { const { data: projectFavorites } = useGetUserProjectFavorites(currentOrg?.id!); const { mutateAsync: updateUserProjectFavorites } = useUpdateUserProjectFavorites(); + const [shouldShowMfa, toggleShowMfa] = useToggle(false); + const [mfaSuccessCallback, setMfaSuccessCallback] = useState<() => void>(() => {}); const workspacesWithFaveProp = useMemo( () => @@ -206,10 +210,17 @@ export const AppLayout = ({ children }: LayoutProps) => { }; const changeOrg = async (orgId: string) => { - await selectOrganization({ + const { token, isMfaEnabled } = await selectOrganization({ organizationId: orgId }); + if (isMfaEnabled) { + SecurityClient.setMfaToken(token); + toggleShowMfa.on(); + setMfaSuccessCallback(() => () => changeOrg(orgId)); + return; + } + await navigateUserToOrg(router, orgId); }; @@ -334,6 +345,18 @@ export const AppLayout = ({ children }: LayoutProps) => { } }; + if (shouldShowMfa) { + return ( +