From 75d9e0b93ae9fdbccc4ff74daf38cd01318631d7 Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Tue, 14 Jul 2026 17:16:01 +0200 Subject: [PATCH] fix(front): constrain 2FA sign-in screens and dedupe their shared shell (#22886) ## Problem On the card-less onboarding sign-in (`/welcome`), the **2FA verification** step renders with a full-viewport-wide submit button. The 2FA **verify** and **provision** forms hard-code `width: 100%` on their root `StyledForm`. That was harmless while `/welcome` rendered inside the `AuthModal` `medium` card, which bounded the width. Since #22398 removed v1 onboarding, `/welcome` renders card-less under `BlankLayout`, so nothing bounds those forms and they stretch to the full viewport. Other steps are **not** affected, which is why only 2FA looks wrong: - Sign-in form: root sets `width: ONBOARDING_CONTENT_BLOCK_WIDTH; max-width: 100%` -> capped at 440. - SSO selection / workspace-scope: base container (`min-width: 240`, no width) -> shrink-to-fit. - **2FA verify / provision: `width: 100%` -> full viewport.** ## Fix Cap the two 2FA forms at `ONBOARDING_CONTENT_BLOCK_WIDTH` (with `max-width: 100%`), so they sit in the same block as the sign-in page instead of forcing full-width. ## Refactor (same PR) The verify and provision components (both introduced together in #13141) duplicated their layout shell. Extracted the shared instruction-text and main-content blocks into `SignInUpTwoFactorAuthenticationStyles.ts`. The form container stays local to each component since the element differs (a `div` in provision, a `form` in verification). ## Verification - Reproduced the flex box-model at 1280px: `width:100%` root -> 1196px full-width button; `width: 440px` -> 440px centered button. - lint + format + typecheck pass on the changed files. --- ...gnInUpTwoFactorAuthenticationProvision.tsx | 41 +++++-------------- .../SignInUpTwoFactorAuthenticationStyles.ts | 26 ++++++++++++ ...nUpTwoFactorAuthenticationVerification.tsx | 31 +++++--------- 3 files changed, 48 insertions(+), 50 deletions(-) create mode 100644 packages/twenty-front/src/modules/auth/sign-in-up/components/internal/SignInUpTwoFactorAuthenticationStyles.ts diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/components/internal/SignInUpTwoFactorAuthenticationProvision.tsx b/packages/twenty-front/src/modules/auth/sign-in-up/components/internal/SignInUpTwoFactorAuthenticationProvision.tsx index 2a8aaa30be..6a2ba3f33a 100644 --- a/packages/twenty-front/src/modules/auth/sign-in-up/components/internal/SignInUpTwoFactorAuthenticationProvision.tsx +++ b/packages/twenty-front/src/modules/auth/sign-in-up/components/internal/SignInUpTwoFactorAuthenticationProvision.tsx @@ -4,6 +4,11 @@ import { SignInUpStep, signInUpStepState, } from '@/auth/states/signInUpStepState'; +import { + StyledTwoFactorInstructions, + StyledTwoFactorMainContent, +} from '@/auth/sign-in-up/components/internal/SignInUpTwoFactorAuthenticationStyles'; +import { ONBOARDING_CONTENT_BLOCK_WIDTH } from '@/onboarding/constants/OnboardingContentBlockWidth'; import { extractSecretFromOtpUri } from '@/settings/two-factor-authentication/utils/extractSecretFromOtpUri'; import { styled } from '@linaria/react'; import { Trans, useLingui } from '@lingui/react/macro'; @@ -20,36 +25,12 @@ import { useCopyToClipboard } from '~/hooks/useCopyToClipboard'; const QRCode = resolveCjsModuleDefaultExport(QRCodeModule); -const StyledMainContentContainer = styled.div` - margin-bottom: ${themeCssVariables.spacing[8]}; - margin-top: ${themeCssVariables.spacing[4]}; - text-align: center; -`; - -const StyledTextContainer = styled.div` - align-items: center; - color: ${themeCssVariables.font.color.tertiary}; - font-size: ${themeCssVariables.font.size.sm}; - - margin-bottom: ${themeCssVariables.spacing[4]}; - max-width: 280px; - text-align: center; - - & > a { - color: ${themeCssVariables.font.color.tertiary}; - text-decoration: none; - - &:hover { - text-decoration: underline; - } - } -`; - const StyledForm = styled.div` align-items: center; display: flex; flex-direction: column; - width: 100%; + max-width: 100%; + width: ${ONBOARDING_CONTENT_BLOCK_WIDTH}px; `; const StyledCopySetupKeyLink = styled.button` @@ -94,13 +75,13 @@ export const SignInUpTwoFactorAuthenticationProvision = () => { <> - + Use authenticator apps and browser extensions like 1Password, Authy, Microsoft Authenticator to generate one-time passwords - - + + {!qrCode ? : } {qrCode && ( @@ -108,7 +89,7 @@ export const SignInUpTwoFactorAuthenticationProvision = () => { Copy Setup Key )} - + a { + color: ${themeCssVariables.font.color.tertiary}; + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } +`; diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/components/internal/SignInUpTwoFactorAuthenticationVerification.tsx b/packages/twenty-front/src/modules/auth/sign-in-up/components/internal/SignInUpTwoFactorAuthenticationVerification.tsx index 9a43669fcf..e769c0d098 100644 --- a/packages/twenty-front/src/modules/auth/sign-in-up/components/internal/SignInUpTwoFactorAuthenticationVerification.tsx +++ b/packages/twenty-front/src/modules/auth/sign-in-up/components/internal/SignInUpTwoFactorAuthenticationVerification.tsx @@ -10,7 +10,12 @@ import { SignInUpStep, signInUpStepState, } from '@/auth/states/signInUpStepState'; +import { + StyledTwoFactorInstructions, + StyledTwoFactorMainContent, +} from '@/auth/sign-in-up/components/internal/SignInUpTwoFactorAuthenticationStyles'; import { useReadCaptchaToken } from '@/captcha/hooks/useReadCaptchaToken'; +import { ONBOARDING_CONTENT_BLOCK_WIDTH } from '@/onboarding/constants/OnboardingContentBlockWidth'; import { useCaptcha } from '@/client-config/hooks/useCaptcha'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { Trans, useLingui } from '@lingui/react/macro'; @@ -25,17 +30,12 @@ import { useNavigateApp } from '~/hooks/useNavigateApp'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; -const StyledMainContentContainer = styled.div` - margin-bottom: ${themeCssVariables.spacing[8]}; - margin-top: ${themeCssVariables.spacing[4]}; - text-align: center; -`; - const StyledForm = styled.form` align-items: center; display: flex; flex-direction: column; - width: 100%; + max-width: 100%; + width: ${ONBOARDING_CONTENT_BLOCK_WIDTH}px; `; const StyledSlot = styled.div<{ isActive: boolean }>` @@ -161,15 +161,6 @@ const StyledOTPContainer = styled.div` const StyledSlotGroup = styled.div` display: flex; `; -const StyledTextContainer = styled.div` - align-items: center; - color: ${themeCssVariables.font.color.tertiary}; - font-size: ${themeCssVariables.font.size.sm}; - - margin-bottom: ${themeCssVariables.spacing[4]}; - max-width: 280px; - text-align: center; -`; const StyledActionBackLinkContainer = styled.div` margin: ${themeCssVariables.spacing[3]} 0 0; @@ -228,10 +219,10 @@ export const SignInUpTOTPVerification = () => { return ( - + Paste the code below - - + + {/* // oxlint-disable-next-line react/jsx-props-no-spreading */} { /> )} /> - +