diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 07b3bbcc02..73ff4e9954 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -1,5 +1,5 @@ -import * as Apollo from '@apollo/client'; import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; export type Maybe = T | null; export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; @@ -169,6 +169,7 @@ export type ClientConfig = { __typename?: 'ClientConfig'; analyticsEnabled: Scalars['Boolean']; api: ApiConfig; + authProviders: AuthProviders; billing: Billing; captcha: Captcha; chromeExtensionId?: Maybe; @@ -1577,7 +1578,7 @@ export type Field = { id: Scalars['UUID']; isActive?: Maybe; isCustom?: Maybe; - isLabelSyncedWithName: Scalars['Boolean']; + isLabelSyncedWithName?: Maybe; isNullable?: Maybe; isSystem?: Maybe; isUnique?: Maybe; @@ -1970,7 +1971,7 @@ export type UpdateBillingSubscriptionMutation = { __typename?: 'Mutation', updat export type GetClientConfigQueryVariables = Exact<{ [key: string]: never; }>; -export type GetClientConfigQuery = { __typename?: 'Query', clientConfig: { __typename?: 'ClientConfig', signInPrefilled: boolean, isMultiWorkspaceEnabled: boolean, isSSOEnabled: boolean, defaultSubdomain?: string | null, frontDomain: string, debugMode: boolean, analyticsEnabled: boolean, chromeExtensionId?: string | null, billing: { __typename?: 'Billing', isBillingEnabled: boolean, billingUrl?: string | null, billingFreeTrialDurationInDays?: number | null }, support: { __typename?: 'Support', supportDriver: string, supportFrontChatId?: string | null }, sentry: { __typename?: 'Sentry', dsn?: string | null, environment?: string | null, release?: string | null }, captcha: { __typename?: 'Captcha', provider?: CaptchaDriverType | null, siteKey?: string | null }, api: { __typename?: 'ApiConfig', mutationMaximumAffectedRecords: number } } }; +export type GetClientConfigQuery = { __typename?: 'Query', clientConfig: { __typename?: 'ClientConfig', signInPrefilled: boolean, isMultiWorkspaceEnabled: boolean, isSSOEnabled: boolean, defaultSubdomain?: string | null, frontDomain: string, debugMode: boolean, analyticsEnabled: boolean, chromeExtensionId?: string | null, billing: { __typename?: 'Billing', isBillingEnabled: boolean, billingUrl?: string | null, billingFreeTrialDurationInDays?: number | null }, authProviders: { __typename?: 'AuthProviders', google: boolean, password: boolean, microsoft: boolean, sso: Array<{ __typename?: 'SSOIdentityProvider', id: string, name: string, type: IdentityProviderType, status: SsoIdentityProviderStatus, issuer: string }> }, support: { __typename?: 'Support', supportDriver: string, supportFrontChatId?: string | null }, sentry: { __typename?: 'Sentry', dsn?: string | null, environment?: string | null, release?: string | null }, captcha: { __typename?: 'Captcha', provider?: CaptchaDriverType | null, siteKey?: string | null }, api: { __typename?: 'ApiConfig', mutationMaximumAffectedRecords: number } } }; export type SkipSyncEmailOnboardingStepMutationVariables = Exact<{ [key: string]: never; }>; @@ -3355,6 +3356,18 @@ export const GetClientConfigDocument = gql` billingUrl billingFreeTrialDurationInDays } + authProviders { + google + password + microsoft + sso { + id + name + type + status + issuer + } + } signInPrefilled isMultiWorkspaceEnabled isSSOEnabled diff --git a/packages/twenty-front/src/modules/auth/hooks/__tests__/useAuth.test.tsx b/packages/twenty-front/src/modules/auth/hooks/__tests__/useAuth.test.tsx index c61dbc2f2c..7fea507688 100644 --- a/packages/twenty-front/src/modules/auth/hooks/__tests__/useAuth.test.tsx +++ b/packages/twenty-front/src/modules/auth/hooks/__tests__/useAuth.test.tsx @@ -7,14 +7,14 @@ import { RecoilRoot, useRecoilValue } from 'recoil'; import { iconsState } from 'twenty-ui'; import { useAuth } from '@/auth/hooks/useAuth'; -import { authProvidersState } from '@/client-config/states/authProvidersState'; import { billingState } from '@/client-config/states/billingState'; import { isDebugModeState } from '@/client-config/states/isDebugModeState'; import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState'; import { supportChatState } from '@/client-config/states/supportChatState'; +import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState'; -import { email, mocks, password, results, token } from '../__mocks__/useAuth'; import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState'; +import { email, mocks, password, results, token } from '../__mocks__/useAuth'; const Wrapper = ({ children }: { children: ReactNode }) => ( @@ -77,7 +77,9 @@ describe('useAuth', () => { () => { const client = useApolloClient(); const icons = useRecoilValue(iconsState); - const authProviders = useRecoilValue(authProvidersState); + const workspaceAuthProviders = useRecoilValue( + workspaceAuthProvidersState, + ); const billing = useRecoilValue(billingState); const isDeveloperDefaultSignInPrefilled = useRecoilValue( isDeveloperDefaultSignInPrefilledState, @@ -92,7 +94,7 @@ describe('useAuth', () => { client, state: { icons, - authProviders, + workspaceAuthProviders, billing, isDeveloperDefaultSignInPrefilled, supportChat, @@ -118,7 +120,7 @@ describe('useAuth', () => { const { state } = result.current; expect(state.icons).toEqual({}); - expect(state.authProviders).toEqual({ + expect(state.workspaceAuthProviders).toEqual({ google: true, microsoft: false, magicLink: false, diff --git a/packages/twenty-front/src/modules/auth/hooks/useAuth.ts b/packages/twenty-front/src/modules/auth/hooks/useAuth.ts index dc9c51dce9..84a9796eb2 100644 --- a/packages/twenty-front/src/modules/auth/hooks/useAuth.ts +++ b/packages/twenty-front/src/modules/auth/hooks/useAuth.ts @@ -13,7 +13,6 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadingState'; import { isVerifyPendingState } from '@/auth/states/isVerifyPendingState'; import { workspacesState } from '@/auth/states/workspaces'; -import { authProvidersState } from '@/client-config/states/authProvidersState'; import { billingState } from '@/client-config/states/billingState'; import { captchaProviderState } from '@/client-config/states/captchaProviderState'; import { clientConfigApiStatusState } from '@/client-config/states/clientConfigApiStatusState'; @@ -48,6 +47,7 @@ import { useLastAuthenticatedWorkspaceDomain } from '@/domain-manager/hooks/useL import { useReadWorkspaceSubdomainFromCurrentLocation } from '@/domain-manager/hooks/useReadWorkspaceSubdomainFromCurrentLocation'; import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState'; import { isAppWaitingForFreshObjectMetadataState } from '@/object-metadata/states/isAppWaitingForFreshObjectMetadataState'; +import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState'; export const useAuth = () => { const setTokenPair = useSetRecoilState(tokenPairState); @@ -90,7 +90,7 @@ export const useAuth = () => { const emptySnapshot = snapshot_UNSTABLE(); const iconsValue = snapshot.getLoadable(iconsState).getValue(); const authProvidersValue = snapshot - .getLoadable(authProvidersState) + .getLoadable(workspaceAuthProvidersState) .getValue(); const billing = snapshot.getLoadable(billingState).getValue(); const isDeveloperDefaultSignInPrefilled = snapshot @@ -115,7 +115,7 @@ export const useAuth = () => { .getValue(); const initialSnapshot = emptySnapshot.map(({ set }) => { set(iconsState, iconsValue); - set(authProvidersState, authProvidersValue); + set(workspaceAuthProvidersState, authProvidersValue); set(billingState, billing); set( isDeveloperDefaultSignInPrefilledState, diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/components/SignInUpGlobalScopeForm.tsx b/packages/twenty-front/src/modules/auth/sign-in-up/components/SignInUpGlobalScopeForm.tsx index 08eb15e34f..870754bab9 100644 --- a/packages/twenty-front/src/modules/auth/sign-in-up/components/SignInUpGlobalScopeForm.tsx +++ b/packages/twenty-front/src/modules/auth/sign-in-up/components/SignInUpGlobalScopeForm.tsx @@ -1,37 +1,31 @@ import styled from '@emotion/styled'; -import { - IconGoogle, - IconMicrosoft, - Loader, - MainButton, - HorizontalSeparator, -} from 'twenty-ui'; -import { useTheme } from '@emotion/react'; -import { useSignInWithGoogle } from '@/auth/sign-in-up/hooks/useSignInWithGoogle'; -import { useSignInWithMicrosoft } from '@/auth/sign-in-up/hooks/useSignInWithMicrosoft'; -import { FormProvider } from 'react-hook-form'; import { motion } from 'framer-motion'; import { useState } from 'react'; -import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; +import { FormProvider } from 'react-hook-form'; import { useLocation } from 'react-router-dom'; +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; +import { HorizontalSeparator, Loader, MainButton } from 'twenty-ui'; -import { isDefined } from '~/utils/isDefined'; +import { useAuth } from '@/auth/hooks/useAuth'; +import { SignInUpEmailField } from '@/auth/sign-in-up/components/SignInUpEmailField'; +import { SignInUpPasswordField } from '@/auth/sign-in-up/components/SignInUpPasswordField'; +import { SignInUpWithGoogle } from '@/auth/sign-in-up/components/SignInUpWithGoogle'; +import { SignInUpWithMicrosoft } from '@/auth/sign-in-up/components/SignInUpWithMicrosoft'; +import { useSignInUp } from '@/auth/sign-in-up/hooks/useSignInUp'; +import { useSignInUpForm } from '@/auth/sign-in-up/hooks/useSignInUpForm'; +import { signInUpModeState } from '@/auth/states/signInUpModeState'; import { SignInUpStep, signInUpStepState, } from '@/auth/states/signInUpStepState'; -import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; -import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; -import { useSignInUp } from '@/auth/sign-in-up/hooks/useSignInUp'; -import { useSignInUpForm } from '@/auth/sign-in-up/hooks/useSignInUpForm'; -import { SignInUpEmailField } from '@/auth/sign-in-up/components/SignInUpEmailField'; -import { SignInUpPasswordField } from '@/auth/sign-in-up/components/SignInUpPasswordField'; -import { useAuth } from '@/auth/hooks/useAuth'; -import { useReadCaptchaToken } from '@/captcha/hooks/useReadCaptchaToken'; -import { signInUpModeState } from '@/auth/states/signInUpModeState'; -import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken'; import { SignInUpMode } from '@/auth/types/signInUpMode'; +import { useReadCaptchaToken } from '@/captcha/hooks/useReadCaptchaToken'; +import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken'; +import { authProvidersState } from '@/client-config/states/authProvidersState'; import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; +import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; +import { isDefined } from '~/utils/isDefined'; const StyledContentContainer = styled(motion.div)` margin-bottom: ${({ theme }) => theme.spacing(8)}; @@ -46,11 +40,9 @@ const StyledForm = styled.form` `; export const SignInUpGlobalScopeForm = () => { - const theme = useTheme(); + const authProviders = useRecoilValue(authProvidersState); const signInUpStep = useRecoilValue(signInUpStepState); - const { signInWithGoogle } = useSignInWithGoogle(); - const { signInWithMicrosoft } = useSignInWithMicrosoft(); const { checkUserExists } = useAuth(); const { readCaptchaToken } = useReadCaptchaToken(); const { redirectToWorkspaceDomain } = useRedirectToWorkspaceDomain(); @@ -116,20 +108,9 @@ export const SignInUpGlobalScopeForm = () => { return ( <> - } - title="Continue with Google" - onClick={signInWithGoogle} - fullWidth - /> - - } - title="Continue with Microsoft" - onClick={signInWithMicrosoft} - fullWidth - /> - + {authProviders.google && } + + {authProviders.microsoft && } {/* eslint-disable-next-line react/jsx-props-no-spreading */} diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/components/SignInUpSSOIdentityProviderSelection.tsx b/packages/twenty-front/src/modules/auth/sign-in-up/components/SignInUpSSOIdentityProviderSelection.tsx index 8a0baa00c0..b3696dde14 100644 --- a/packages/twenty-front/src/modules/auth/sign-in-up/components/SignInUpSSOIdentityProviderSelection.tsx +++ b/packages/twenty-front/src/modules/auth/sign-in-up/components/SignInUpSSOIdentityProviderSelection.tsx @@ -4,10 +4,10 @@ import { useSSO } from '@/auth/sign-in-up/hooks/useSSO'; import { guessSSOIdentityProviderIconByUrl } from '@/settings/security/utils/guessSSOIdentityProviderIconByUrl'; import styled from '@emotion/styled'; import { useRecoilValue } from 'recoil'; -import { MainButton, HorizontalSeparator } from 'twenty-ui'; +import { HorizontalSeparator, MainButton } from 'twenty-ui'; +import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState'; import { isDefined } from '~/utils/isDefined'; -import { authProvidersState } from '@/client-config/states/authProvidersState'; const StyledContentContainer = styled.div` margin-bottom: ${({ theme }) => theme.spacing(8)}; @@ -15,15 +15,15 @@ const StyledContentContainer = styled.div` `; export const SignInUpSSOIdentityProviderSelection = () => { - const authProviders = useRecoilValue(authProvidersState); + const workspaceAuthProviders = useRecoilValue(workspaceAuthProvidersState); const { redirectToSSOLoginPage } = useSSO(); return ( <> - {isDefined(authProviders?.sso) && - authProviders?.sso.map((idp) => ( + {isDefined(workspaceAuthProviders?.sso) && + workspaceAuthProviders?.sso.map((idp) => ( <> { const theme = useTheme(); const setSignInUpStep = useSetRecoilState(signInUpStepState); - const authProviders = useRecoilValue(authProvidersState); + const workspaceAuthProviders = useRecoilValue(workspaceAuthProvidersState); const signInUpStep = useRecoilValue(signInUpStepState); const { redirectToSSOLoginPage } = useSSO(); const signInWithSSO = () => { - if (authProviders.sso.length === 1) { - return redirectToSSOLoginPage(authProviders.sso[0].id); + if (workspaceAuthProviders.sso.length === 1) { + return redirectToSSOLoginPage(workspaceAuthProviders.sso[0].id); } setSignInUpStep(SignInUpStep.SSOIdentityProviderSelection); diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/components/SignInUpWorkspaceScopeForm.tsx b/packages/twenty-front/src/modules/auth/sign-in-up/components/SignInUpWorkspaceScopeForm.tsx index 877598a8bc..26120c8749 100644 --- a/packages/twenty-front/src/modules/auth/sign-in-up/components/SignInUpWorkspaceScopeForm.tsx +++ b/packages/twenty-front/src/modules/auth/sign-in-up/components/SignInUpWorkspaceScopeForm.tsx @@ -1,15 +1,15 @@ +import { SignInUpWithCredentials } from '@/auth/sign-in-up/components/SignInUpWithCredentials'; +import { SignInUpWithGoogle } from '@/auth/sign-in-up/components/SignInUpWithGoogle'; +import { SignInUpWithMicrosoft } from '@/auth/sign-in-up/components/SignInUpWithMicrosoft'; +import { SignInUpWithSSO } from '@/auth/sign-in-up/components/SignInUpWithSSO'; import { useHandleResetPassword } from '@/auth/sign-in-up/hooks/useHandleResetPassword'; import { useSignInUp } from '@/auth/sign-in-up/hooks/useSignInUp'; import { useSignInUpForm } from '@/auth/sign-in-up/hooks/useSignInUpForm'; import { SignInUpStep } from '@/auth/states/signInUpStepState'; -import { authProvidersState } from '@/client-config/states/authProvidersState'; +import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState'; import styled from '@emotion/styled'; -import { useRecoilState } from 'recoil'; +import { useRecoilValue } from 'recoil'; import { ActionLink, HorizontalSeparator } from 'twenty-ui'; -import { SignInUpWithGoogle } from '@/auth/sign-in-up/components/SignInUpWithGoogle'; -import { SignInUpWithMicrosoft } from '@/auth/sign-in-up/components/SignInUpWithMicrosoft'; -import { SignInUpWithSSO } from '@/auth/sign-in-up/components/SignInUpWithSSO'; -import { SignInUpWithCredentials } from '@/auth/sign-in-up/components/SignInUpWithCredentials'; const StyledContentContainer = styled.div` margin-bottom: ${({ theme }) => theme.spacing(8)}; @@ -17,7 +17,7 @@ const StyledContentContainer = styled.div` `; export const SignInUpWorkspaceScopeForm = () => { - const [authProviders] = useRecoilState(authProvidersState); + const workspaceAuthProviders = useRecoilValue(workspaceAuthProvidersState); const { form } = useSignInUpForm(); const { handleResetPassword } = useHandleResetPassword(); @@ -27,20 +27,20 @@ export const SignInUpWorkspaceScopeForm = () => { return ( <> - {authProviders.google && } + {workspaceAuthProviders.google && } - {authProviders.microsoft && } + {workspaceAuthProviders.microsoft && } - {authProviders.sso.length > 0 && } + {workspaceAuthProviders.sso.length > 0 && } - {(authProviders.google || - authProviders.microsoft || - authProviders.sso.length > 0) && - authProviders.password ? ( + {(workspaceAuthProviders.google || + workspaceAuthProviders.microsoft || + workspaceAuthProviders.sso.length > 0) && + workspaceAuthProviders.password ? ( ) : null} - {authProviders.password && } + {workspaceAuthProviders.password && } {signInUpStep === SignInUpStep.Password && ( diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/components/SignInUpWorkspaceScopeFormEffect.tsx b/packages/twenty-front/src/modules/auth/sign-in-up/components/SignInUpWorkspaceScopeFormEffect.tsx index c3be61dfd8..530a9dee56 100644 --- a/packages/twenty-front/src/modules/auth/sign-in-up/components/SignInUpWorkspaceScopeFormEffect.tsx +++ b/packages/twenty-front/src/modules/auth/sign-in-up/components/SignInUpWorkspaceScopeFormEffect.tsx @@ -1,16 +1,16 @@ -import { SignInUpStep } from '@/auth/states/signInUpStepState'; -import { isDefined } from '~/utils/isDefined'; import { useSignInUp } from '@/auth/sign-in-up/hooks/useSignInUp'; -import { authProvidersState } from '@/client-config/states/authProvidersState'; import { useSignInUpForm } from '@/auth/sign-in-up/hooks/useSignInUpForm'; -import { useRecoilState } from 'recoil'; +import { SignInUpStep } from '@/auth/states/signInUpStepState'; +import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState'; import { useCallback, useEffect } from 'react'; +import { useRecoilValue } from 'recoil'; +import { isDefined } from '~/utils/isDefined'; const searchParams = new URLSearchParams(window.location.search); const email = searchParams.get('email'); export const SignInUpWorkspaceScopeFormEffect = () => { - const [authProviders] = useRecoilState(authProvidersState); + const workspaceAuthProviders = useRecoilValue(workspaceAuthProvidersState); const { form } = useSignInUpForm(); @@ -20,22 +20,22 @@ export const SignInUpWorkspaceScopeFormEffect = () => { const checkAuthProviders = useCallback(() => { if ( signInUpStep === SignInUpStep.Init && - !authProviders.google && - !authProviders.microsoft && - !authProviders.sso + !workspaceAuthProviders.google && + !workspaceAuthProviders.microsoft && + !workspaceAuthProviders.sso ) { return continueWithEmail(); } - if (isDefined(email) && authProviders.password) { + if (isDefined(email) && workspaceAuthProviders.password) { return continueWithCredentials(); } }, [ signInUpStep, - authProviders.google, - authProviders.microsoft, - authProviders.sso, - authProviders.password, + workspaceAuthProviders.google, + workspaceAuthProviders.microsoft, + workspaceAuthProviders.sso, + workspaceAuthProviders.password, continueWithEmail, continueWithCredentials, ]); diff --git a/packages/twenty-front/src/modules/client-config/components/ClientConfigProviderEffect.tsx b/packages/twenty-front/src/modules/client-config/components/ClientConfigProviderEffect.tsx index 560974fe36..ebbfa965ea 100644 --- a/packages/twenty-front/src/modules/client-config/components/ClientConfigProviderEffect.tsx +++ b/packages/twenty-front/src/modules/client-config/components/ClientConfigProviderEffect.tsx @@ -1,4 +1,5 @@ import { apiConfigState } from '@/client-config/states/apiConfigState'; +import { authProvidersState } from '@/client-config/states/authProvidersState'; import { billingState } from '@/client-config/states/billingState'; import { captchaProviderState } from '@/client-config/states/captchaProviderState'; import { chromeExtensionIdState } from '@/client-config/states/chromeExtensionIdState'; @@ -7,19 +8,20 @@ import { isAnalyticsEnabledState } from '@/client-config/states/isAnalyticsEnabl import { isDebugModeState } from '@/client-config/states/isDebugModeState'; import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState'; import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState'; +import { isSSOEnabledState } from '@/client-config/states/isSSOEnabledState'; import { sentryConfigState } from '@/client-config/states/sentryConfigState'; import { supportChatState } from '@/client-config/states/supportChatState'; +import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState'; import { useEffect } from 'react'; import { useRecoilState, useSetRecoilState } from 'recoil'; import { useGetClientConfigQuery } from '~/generated/graphql'; import { isDefined } from '~/utils/isDefined'; -import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState'; -import { isSSOEnabledState } from '@/client-config/states/isSSOEnabledState'; export const ClientConfigProviderEffect = () => { const setIsDebugMode = useSetRecoilState(isDebugModeState); const setIsAnalyticsEnabled = useSetRecoilState(isAnalyticsEnabledState); const setDomainConfiguration = useSetRecoilState(domainConfigurationState); + const setAuthProviders = useSetRecoilState(authProvidersState); const setIsDeveloperDefaultSignInPrefilled = useSetRecoilState( isDeveloperDefaultSignInPrefilledState, @@ -73,6 +75,13 @@ export const ClientConfigProviderEffect = () => { error: undefined, })); + setAuthProviders({ + google: data?.clientConfig.authProviders.google, + microsoft: data?.clientConfig.authProviders.microsoft, + password: data?.clientConfig.authProviders.password, + magicLink: false, + sso: data?.clientConfig.authProviders.sso, + }); setIsDebugMode(data?.clientConfig.debugMode); setIsAnalyticsEnabled(data?.clientConfig.analyticsEnabled); setIsDeveloperDefaultSignInPrefilled(data?.clientConfig.signInPrefilled); @@ -115,6 +124,7 @@ export const ClientConfigProviderEffect = () => { error, setDomainConfiguration, setIsSSOEnabledState, + setAuthProviders, ]); return <>; diff --git a/packages/twenty-front/src/modules/client-config/graphql/queries/getClientConfig.ts b/packages/twenty-front/src/modules/client-config/graphql/queries/getClientConfig.ts index 2c6da152ea..88a6963689 100644 --- a/packages/twenty-front/src/modules/client-config/graphql/queries/getClientConfig.ts +++ b/packages/twenty-front/src/modules/client-config/graphql/queries/getClientConfig.ts @@ -8,6 +8,18 @@ export const GET_CLIENT_CONFIG = gql` billingUrl billingFreeTrialDurationInDays } + authProviders { + google + password + microsoft + sso { + id + name + type + status + issuer + } + } signInPrefilled isMultiWorkspaceEnabled isSSOEnabled diff --git a/packages/twenty-front/src/modules/domain-manager/hooks/useGetPublicWorkspaceDataBySubdomain.ts b/packages/twenty-front/src/modules/domain-manager/hooks/useGetPublicWorkspaceDataBySubdomain.ts index e9bba69aec..e61ff3730d 100644 --- a/packages/twenty-front/src/modules/domain-manager/hooks/useGetPublicWorkspaceDataBySubdomain.ts +++ b/packages/twenty-front/src/modules/domain-manager/hooks/useGetPublicWorkspaceDataBySubdomain.ts @@ -1,17 +1,19 @@ -import { useGetPublicWorkspaceDataBySubdomainQuery } from '~/generated/graphql'; -import { isDefined } from '~/utils/isDefined'; -import { authProvidersState } from '@/client-config/states/authProvidersState'; import { workspacePublicDataState } from '@/auth/states/workspacePublicDataState'; -import { useRecoilValue, useSetRecoilState } from 'recoil'; -import { useRedirectToDefaultDomain } from '@/domain-manager/hooks/useRedirectToDefaultDomain'; -import { useLastAuthenticatedWorkspaceDomain } from '@/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain'; import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState'; import { useIsCurrentLocationOnDefaultDomain } from '@/domain-manager/hooks/useIsCurrentLocationOnDefaultDomain'; +import { useLastAuthenticatedWorkspaceDomain } from '@/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain'; +import { useRedirectToDefaultDomain } from '@/domain-manager/hooks/useRedirectToDefaultDomain'; +import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState'; +import { useRecoilValue, useSetRecoilState } from 'recoil'; +import { useGetPublicWorkspaceDataBySubdomainQuery } from '~/generated/graphql'; +import { isDefined } from '~/utils/isDefined'; export const useGetPublicWorkspaceDataBySubdomain = () => { const { isDefaultDomain } = useIsCurrentLocationOnDefaultDomain(); const isMultiWorkspaceEnabled = useRecoilValue(isMultiWorkspaceEnabledState); - const setAuthProviders = useSetRecoilState(authProvidersState); + const setWorkspaceAuthProviders = useSetRecoilState( + workspaceAuthProvidersState, + ); const workspacePublicData = useRecoilValue(workspacePublicDataState); const { redirectToDefaultDomain } = useRedirectToDefaultDomain(); const setWorkspacePublicDataState = useSetRecoilState( @@ -25,7 +27,9 @@ export const useGetPublicWorkspaceDataBySubdomain = () => { (isMultiWorkspaceEnabled && isDefaultDomain) || isDefined(workspacePublicData), onCompleted: (data) => { - setAuthProviders(data.getPublicWorkspaceDataBySubdomain.authProviders); + setWorkspaceAuthProviders( + data.getPublicWorkspaceDataBySubdomain.authProviders, + ); setWorkspacePublicDataState(data.getPublicWorkspaceDataBySubdomain); }, onError: (error) => { diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/__stories__/perf/RecordTableCell.perf.stories.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/__stories__/perf/RecordTableCell.perf.stories.tsx index 9c9e9567da..4989c848a6 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/__stories__/perf/RecordTableCell.perf.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/__stories__/perf/RecordTableCell.perf.stories.tsx @@ -18,6 +18,7 @@ import { ChipGeneratorsDecorator } from '~/testing/decorators/ChipGeneratorsDeco import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator'; import { getProfilingStory } from '~/testing/profiling/utils/getProfilingStory'; +import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext'; import { RecordTableBodyContextProvider } from '@/object-record/record-table/contexts/RecordTableBodyContext'; import { RecordTableContextProvider } from '@/object-record/record-table/contexts/RecordTableContext'; import { RecordTableRowContextProvider } from '@/object-record/record-table/contexts/RecordTableRowContext'; @@ -63,87 +64,98 @@ const meta: Meta = { (Story) => { return ( - '', + onIndexRecordsLoaded: () => {}, + objectNamePlural: 'companies', + objectNameSingular: 'company', objectMetadataItem: mockPerformance.objectMetadataItem as any, - visibleTableColumns: mockPerformance.visibleTableColumns as any, - objectNameSingular: - mockPerformance.objectMetadataItem.nameSingular, + recordIndexId: 'recordIndexId', }} > - {}} + - {}, - onOpenTableCell: () => {}, - onMoveFocus: () => {}, - onCloseTableCell: () => {}, - onMoveSoftFocusToCell: () => {}, - onActionMenuDropdownOpened: () => {}, - onCellMouseEnter: () => {}, - }} + {}} > - {}, + onOpenTableCell: () => {}, + onMoveFocus: () => {}, + onCloseTableCell: () => {}, + onMoveSoftFocusToCell: () => {}, + onActionMenuDropdownOpened: () => {}, + onCellMouseEnter: () => {}, }} > - - - - - - - - - - -
-
-
-
-
-
-
-
+ + + + + + + + +
+
+ + + + + + +
); }, diff --git a/packages/twenty-front/src/modules/settings/security/components/SettingsSecurityOptionsList.tsx b/packages/twenty-front/src/modules/settings/security/components/SettingsSecurityOptionsList.tsx index e2dc2d092a..3b19c42178 100644 --- a/packages/twenty-front/src/modules/settings/security/components/SettingsSecurityOptionsList.tsx +++ b/packages/twenty-front/src/modules/settings/security/components/SettingsSecurityOptionsList.tsx @@ -1,4 +1,5 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; +import { authProvidersState } from '@/client-config/states/authProvidersState'; import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle'; import { SSOIdentitiesProvidersState } from '@/settings/security/states/SSOIdentitiesProvidersState'; import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; @@ -25,6 +26,7 @@ const StyledSettingsSecurityOptionsList = styled.div` export const SettingsSecurityOptionsList = () => { const { enqueueSnackBar } = useSnackBar(); const SSOIdentitiesProviders = useRecoilValue(SSOIdentitiesProvidersState); + const authProviders = useRecoilValue(authProvidersState); const [currentWorkspace, setCurrentWorkspace] = useRecoilState( currentWorkspaceState, @@ -123,32 +125,38 @@ export const SettingsSecurityOptionsList = () => { {currentWorkspace && ( <> - toggleAuthMethod('google')} - /> - toggleAuthMethod('microsoft')} - /> - toggleAuthMethod('password')} - /> + {authProviders.google === true && ( + toggleAuthMethod('google')} + /> + )} + {authProviders.microsoft === true && ( + toggleAuthMethod('microsoft')} + /> + )} + {authProviders.password === true && ( + toggleAuthMethod('password')} + /> + )} ({ + key: 'workspaceAuthProvidersState', + defaultValue: { + google: true, + magicLink: false, + password: true, + microsoft: false, + sso: [], + }, +}); diff --git a/packages/twenty-front/src/testing/mock-data/config.ts b/packages/twenty-front/src/testing/mock-data/config.ts index f3235dfc43..a80303d9a5 100644 --- a/packages/twenty-front/src/testing/mock-data/config.ts +++ b/packages/twenty-front/src/testing/mock-data/config.ts @@ -1,10 +1,17 @@ -import { ClientConfig } from '~/generated-metadata/graphql'; -import { CaptchaDriverType } from '~/generated/graphql'; +import { CaptchaDriverType, ClientConfig } from '~/generated/graphql'; export const mockedClientConfig: ClientConfig = { signInPrefilled: true, isMultiWorkspaceEnabled: false, isSSOEnabled: false, + authProviders: { + google: true, + magicLink: false, + password: true, + microsoft: false, + sso: [], + __typename: 'AuthProviders', + }, frontDomain: 'localhost', defaultSubdomain: 'app', chromeExtensionId: 'MOCKED_EXTENSION_ID', diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/switch-workspace.service.spec.ts b/packages/twenty-server/src/engine/core-modules/auth/services/switch-workspace.service.spec.ts index 56cf23c0d1..749a70035c 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/switch-workspace.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/switch-workspace.service.spec.ts @@ -6,9 +6,10 @@ import { Repository } from 'typeorm'; import { AuthException } from 'src/engine/core-modules/auth/auth.exception'; import { AccessTokenService } from 'src/engine/core-modules/auth/token/services/access-token.service'; import { RefreshTokenService } from 'src/engine/core-modules/auth/token/services/refresh-token.service'; +import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service'; +import { UserService } from 'src/engine/core-modules/user/services/user.service'; import { User } from 'src/engine/core-modules/user/user.entity'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; -import { UserService } from 'src/engine/core-modules/user/services/user.service'; import { SwitchWorkspaceService } from './switch-workspace.service'; @@ -50,6 +51,12 @@ describe('SwitchWorkspaceService', () => { saveDefaultWorkspaceIfUserHasAccessOrThrow: jest.fn(), }, }, + { + provide: EnvironmentService, + useValue: { + get: jest.fn(), + }, + }, ], }).compile(); diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/switch-workspace.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/switch-workspace.service.ts index 6a88dfb9a8..bdc324fed9 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/switch-workspace.service.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/switch-workspace.service.ts @@ -7,13 +7,15 @@ import { AuthException, AuthExceptionCode, } from 'src/engine/core-modules/auth/auth.exception'; +import { AuthTokens } from 'src/engine/core-modules/auth/dto/token.entity'; import { AccessTokenService } from 'src/engine/core-modules/auth/token/services/access-token.service'; import { RefreshTokenService } from 'src/engine/core-modules/auth/token/services/refresh-token.service'; -import { User } from 'src/engine/core-modules/user/user.entity'; -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; -import { AuthTokens } from 'src/engine/core-modules/auth/dto/token.entity'; +import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service'; import { UserService } from 'src/engine/core-modules/user/services/user.service'; -import { getAuthProvidersByWorkspace } from 'src/engine/core-modules/workspace/utils/getAuthProvidersByWorkspace'; +import { User } from 'src/engine/core-modules/user/user.entity'; +import { AuthProviders } from 'src/engine/core-modules/workspace/dtos/public-workspace-data.output'; +import { getAuthProvidersByWorkspace } from 'src/engine/core-modules/workspace/utils/get-auth-providers-by-workspace.util'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; @Injectable() export class SwitchWorkspaceService { @@ -25,6 +27,7 @@ export class SwitchWorkspaceService { private readonly userService: UserService, private readonly accessTokenService: AccessTokenService, private readonly refreshTokenService: RefreshTokenService, + private readonly environmentService: EnvironmentService, ) {} async switchWorkspace(user: User, workspaceId: string) { @@ -65,12 +68,23 @@ export class SwitchWorkspaceService { defaultWorkspace: workspace, }); + const systemEnabledProviders: AuthProviders = { + google: this.environmentService.get('AUTH_GOOGLE_ENABLED'), + magicLink: false, + password: this.environmentService.get('AUTH_PASSWORD_ENABLED'), + microsoft: this.environmentService.get('AUTH_MICROSOFT_ENABLED'), + sso: [], + }; + return { id: workspace.id, subdomain: workspace.subdomain, logo: workspace.logo, displayName: workspace.displayName, - authProviders: getAuthProvidersByWorkspace(workspace), + authProviders: getAuthProvidersByWorkspace({ + workspace, + systemEnabledProviders, + }), }; } diff --git a/packages/twenty-server/src/engine/core-modules/client-config/client-config.entity.ts b/packages/twenty-server/src/engine/core-modules/client-config/client-config.entity.ts index 13ec58e69b..e67b4052f8 100644 --- a/packages/twenty-server/src/engine/core-modules/client-config/client-config.entity.ts +++ b/packages/twenty-server/src/engine/core-modules/client-config/client-config.entity.ts @@ -1,6 +1,7 @@ import { Field, ObjectType } from '@nestjs/graphql'; import { CaptchaDriverType } from 'src/engine/core-modules/captcha/interfaces'; +import { AuthProviders } from 'src/engine/core-modules/workspace/dtos/public-workspace-data.output'; @ObjectType() class Billing { @@ -52,6 +53,9 @@ class ApiConfig { @ObjectType() export class ClientConfig { + @Field(() => AuthProviders, { nullable: false }) + authProviders: AuthProviders; + @Field(() => Billing, { nullable: false }) billing: Billing; diff --git a/packages/twenty-server/src/engine/core-modules/client-config/client-config.resolver.ts b/packages/twenty-server/src/engine/core-modules/client-config/client-config.resolver.ts index ef8fcfe730..cbd1fb7d01 100644 --- a/packages/twenty-server/src/engine/core-modules/client-config/client-config.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/client-config/client-config.resolver.ts @@ -22,6 +22,13 @@ export class ClientConfigResolver { 'BILLING_FREE_TRIAL_DURATION_IN_DAYS', ), }, + authProviders: { + google: this.environmentService.get('AUTH_GOOGLE_ENABLED'), + magicLink: false, + password: this.environmentService.get('AUTH_PASSWORD_ENABLED'), + microsoft: this.environmentService.get('AUTH_MICROSOFT_ENABLED'), + sso: [], + }, isSSOEnabled: this.environmentService.get('AUTH_SSO_ENABLED'), signInPrefilled: this.environmentService.get('SIGN_IN_PREFILLED'), isMultiWorkspaceEnabled: this.environmentService.get( diff --git a/packages/twenty-server/src/engine/core-modules/domain-manager/service/domain-manager.service.ts b/packages/twenty-server/src/engine/core-modules/domain-manager/service/domain-manager.service.ts index 5b711fbef7..27814f53ab 100644 --- a/packages/twenty-server/src/engine/core-modules/domain-manager/service/domain-manager.service.ts +++ b/packages/twenty-server/src/engine/core-modules/domain-manager/service/domain-manager.service.ts @@ -23,19 +23,24 @@ export class DomainManagerService { getFrontUrl() { let baseUrl: URL; + const frontPort = this.environmentService.get('FRONT_PORT'); + const frontDomain = this.environmentService.get('FRONT_DOMAIN'); + const frontProtocol = this.environmentService.get('FRONT_PROTOCOL'); - if (!this.environmentService.get('FRONT_DOMAIN')) { - baseUrl = new URL(this.environmentService.get('SERVER_URL')); + const serverUrl = this.environmentService.get('SERVER_URL'); + + if (!frontDomain) { + baseUrl = new URL(serverUrl); } else { - baseUrl = new URL( - `${this.environmentService.get('FRONT_PROTOCOL')}://${this.environmentService.get('FRONT_DOMAIN')}`, - ); + baseUrl = new URL(`${frontProtocol}://${frontDomain}`); + } - const port = this.environmentService.get('FRONT_PORT'); + if (frontPort) { + baseUrl.port = frontPort.toString(); + } - if (port) { - baseUrl.port = port.toString(); - } + if (frontProtocol) { + baseUrl.protocol = frontProtocol; } return baseUrl; diff --git a/packages/twenty-server/src/engine/core-modules/workspace/utils/getAuthProvidersByWorkspace.spec.ts b/packages/twenty-server/src/engine/core-modules/workspace/utils/__tests__/get-auth-providers-by-workspace.util.spec.ts similarity index 68% rename from packages/twenty-server/src/engine/core-modules/workspace/utils/getAuthProvidersByWorkspace.spec.ts rename to packages/twenty-server/src/engine/core-modules/workspace/utils/__tests__/get-auth-providers-by-workspace.util.spec.ts index bd33fc0be4..c8da86b851 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace/utils/getAuthProvidersByWorkspace.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace/utils/__tests__/get-auth-providers-by-workspace.util.spec.ts @@ -1,7 +1,6 @@ +import { getAuthProvidersByWorkspace } from 'src/engine/core-modules/workspace/utils/get-auth-providers-by-workspace.util'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; -import { getAuthProvidersByWorkspace } from './getAuthProvidersByWorkspace'; - describe('getAuthProvidersByWorkspace', () => { const mockWorkspace = { isGoogleAuthEnabled: true, @@ -20,7 +19,14 @@ describe('getAuthProvidersByWorkspace', () => { it('should return correct auth providers for given workspace', () => { const result = getAuthProvidersByWorkspace({ - ...mockWorkspace, + workspace: mockWorkspace, + systemEnabledProviders: { + google: true, + magicLink: false, + password: true, + microsoft: true, + sso: [], + }, }); expect(result).toEqual({ @@ -42,8 +48,14 @@ describe('getAuthProvidersByWorkspace', () => { it('should handle workspace with no SSO providers', () => { const result = getAuthProvidersByWorkspace({ - ...mockWorkspace, - workspaceSSOIdentityProviders: [], + workspace: { ...mockWorkspace, workspaceSSOIdentityProviders: [] }, + systemEnabledProviders: { + google: true, + magicLink: false, + password: true, + microsoft: true, + sso: [], + }, }); expect(result).toEqual({ @@ -57,8 +69,14 @@ describe('getAuthProvidersByWorkspace', () => { it('should disable Microsoft auth if isMicrosoftAuthEnabled is false', () => { const result = getAuthProvidersByWorkspace({ - ...mockWorkspace, - isMicrosoftAuthEnabled: false, + workspace: { ...mockWorkspace, isMicrosoftAuthEnabled: false }, + systemEnabledProviders: { + google: true, + magicLink: false, + password: true, + microsoft: true, + sso: [], + }, }); expect(result).toEqual({ diff --git a/packages/twenty-server/src/engine/core-modules/workspace/utils/workspaceGraphqlApiExceptionHandler.spec.ts b/packages/twenty-server/src/engine/core-modules/workspace/utils/__tests__/workspace-graphql-api-exception-handler.util.spec.ts similarity index 93% rename from packages/twenty-server/src/engine/core-modules/workspace/utils/workspaceGraphqlApiExceptionHandler.spec.ts rename to packages/twenty-server/src/engine/core-modules/workspace/utils/__tests__/workspace-graphql-api-exception-handler.util.spec.ts index 351f5ab87b..f5ae36bf02 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace/utils/workspaceGraphqlApiExceptionHandler.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace/utils/__tests__/workspace-graphql-api-exception-handler.util.spec.ts @@ -3,13 +3,12 @@ import { InternalServerError, NotFoundError, } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; +import { workspaceGraphqlApiExceptionHandler } from 'src/engine/core-modules/workspace/utils/workspace-graphql-api-exception-handler.util'; import { WorkspaceException, WorkspaceExceptionCode, } from 'src/engine/core-modules/workspace/workspace.exception'; -import { workspaceGraphqlApiExceptionHandler } from './workspaceGraphqlApiExceptionHandler'; - describe('workspaceGraphqlApiExceptionHandler', () => { it('should throw NotFoundError when WorkspaceExceptionCode is SUBDOMAIN_NOT_FOUND', () => { const error = new WorkspaceException( diff --git a/packages/twenty-server/src/engine/core-modules/workspace/utils/get-auth-providers-by-workspace.util.ts b/packages/twenty-server/src/engine/core-modules/workspace/utils/get-auth-providers-by-workspace.util.ts new file mode 100644 index 0000000000..0159980343 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/workspace/utils/get-auth-providers-by-workspace.util.ts @@ -0,0 +1,32 @@ +import { AuthProviders } from 'src/engine/core-modules/workspace/dtos/public-workspace-data.output'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; + +export const getAuthProvidersByWorkspace = ({ + workspace, + systemEnabledProviders, +}: { + workspace: Pick< + Workspace, + | 'isGoogleAuthEnabled' + | 'isPasswordAuthEnabled' + | 'isMicrosoftAuthEnabled' + | 'workspaceSSOIdentityProviders' + >; + systemEnabledProviders: AuthProviders; +}) => { + return { + google: workspace.isGoogleAuthEnabled && systemEnabledProviders.google, + magicLink: false, + password: + workspace.isPasswordAuthEnabled && systemEnabledProviders.password, + microsoft: + workspace.isMicrosoftAuthEnabled && systemEnabledProviders.microsoft, + sso: workspace.workspaceSSOIdentityProviders.map((identityProvider) => ({ + id: identityProvider.id, + name: identityProvider.name, + type: identityProvider.type, + status: identityProvider.status, + issuer: identityProvider.issuer, + })), + }; +}; diff --git a/packages/twenty-server/src/engine/core-modules/workspace/utils/getAuthProvidersByWorkspace.ts b/packages/twenty-server/src/engine/core-modules/workspace/utils/getAuthProvidersByWorkspace.ts deleted file mode 100644 index bd2751b790..0000000000 --- a/packages/twenty-server/src/engine/core-modules/workspace/utils/getAuthProvidersByWorkspace.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; - -export const getAuthProvidersByWorkspace = (workspace: Workspace) => { - return { - google: workspace.isGoogleAuthEnabled, - magicLink: false, - password: workspace.isPasswordAuthEnabled, - microsoft: workspace.isMicrosoftAuthEnabled, - sso: workspace.workspaceSSOIdentityProviders.map((identityProvider) => ({ - id: identityProvider.id, - name: identityProvider.name, - type: identityProvider.type, - status: identityProvider.status, - issuer: identityProvider.issuer, - })), - }; -}; diff --git a/packages/twenty-server/src/engine/core-modules/workspace/utils/workspaceGraphqlApiExceptionHandler.ts b/packages/twenty-server/src/engine/core-modules/workspace/utils/workspace-graphql-api-exception-handler.util.ts similarity index 100% rename from packages/twenty-server/src/engine/core-modules/workspace/utils/workspaceGraphqlApiExceptionHandler.ts rename to packages/twenty-server/src/engine/core-modules/workspace/utils/workspace-graphql-api-exception-handler.util.ts diff --git a/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts b/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts index dd6fdd3973..e038c2da8b 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts @@ -23,10 +23,13 @@ import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/use import { User } from 'src/engine/core-modules/user/user.entity'; import { ActivateWorkspaceInput } from 'src/engine/core-modules/workspace/dtos/activate-workspace-input'; import { ActivateWorkspaceOutput } from 'src/engine/core-modules/workspace/dtos/activate-workspace-output'; -import { PublicWorkspaceDataOutput } from 'src/engine/core-modules/workspace/dtos/public-workspace-data.output'; +import { + AuthProviders, + PublicWorkspaceDataOutput, +} from 'src/engine/core-modules/workspace/dtos/public-workspace-data.output'; import { UpdateWorkspaceInput } from 'src/engine/core-modules/workspace/dtos/update-workspace-input'; -import { getAuthProvidersByWorkspace } from 'src/engine/core-modules/workspace/utils/getAuthProvidersByWorkspace'; -import { workspaceGraphqlApiExceptionHandler } from 'src/engine/core-modules/workspace/utils/workspaceGraphqlApiExceptionHandler'; +import { getAuthProvidersByWorkspace } from 'src/engine/core-modules/workspace/utils/get-auth-providers-by-workspace.util'; +import { workspaceGraphqlApiExceptionHandler } from 'src/engine/core-modules/workspace/utils/workspace-graphql-api-exception-handler.util'; import { WorkspaceException, WorkspaceExceptionCode, @@ -207,12 +210,23 @@ export class WorkspaceResolver { } } + const systemEnabledProviders: AuthProviders = { + google: this.environmentService.get('AUTH_GOOGLE_ENABLED'), + magicLink: false, + password: this.environmentService.get('AUTH_PASSWORD_ENABLED'), + microsoft: this.environmentService.get('AUTH_MICROSOFT_ENABLED'), + sso: [], + }; + return { id: workspace.id, logo: workspaceLogoWithToken, displayName: workspace.displayName, subdomain: workspace.subdomain, - authProviders: getAuthProvidersByWorkspace(workspace), + authProviders: getAuthProvidersByWorkspace({ + workspace, + systemEnabledProviders, + }), }; } }