BREAKING - feat(auth): refactor tokens logic & enhance email verification flow (#13487)

- Replaced `getAuthTokensFromLoginToken` with
`getAccessTokensFromLoginToken` for clarity.
- Introduced `getWorkspaceAgnosticTokenFromEmailVerificationToken`.
- Extended mutation inputs to include `locale` and
`verifyEmailNextPath`.
- Added email verification check and sending to various handlers.
- Updated GraphQL types and hooks to reflect these changes.


Fix #13412

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
Antoine Moreaux
2025-07-31 12:00:59 +02:00
committed by GitHub
parent 463e2e89c8
commit 00d12e854a
53 changed files with 594 additions and 306 deletions
@@ -3,7 +3,7 @@ import { AppPath } from '@/types/AppPath';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { ApolloError } from '@apollo/client';
import { verifyEmailNextPathState } from '@/app/states/verifyEmailNextPathState';
import { verifyEmailRedirectPathState } from '@/app/states/verifyEmailRedirectPathState';
import { useVerifyLogin } from '@/auth/hooks/useVerifyLogin';
import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
import { Modal } from '@/ui/layout/modal/components/Modal';
@@ -15,24 +15,31 @@ import { isDefined } from 'twenty-shared/utils';
import { useNavigateApp } from '~/hooks/useNavigateApp';
import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl';
import { EmailVerificationSent } from '../sign-in-up/components/EmailVerificationSent';
import { useIsCurrentLocationOnAWorkspace } from '@/domain-manager/hooks/useIsCurrentLocationOnAWorkspace';
export const VerifyEmailEffect = () => {
const { getLoginTokenFromEmailVerificationToken } = useAuth();
const {
getLoginTokenFromEmailVerificationToken,
getWorkspaceAgnosticTokenFromEmailVerificationToken,
} = useAuth();
const { enqueueErrorSnackBar, enqueueSuccessSnackBar } = useSnackBar();
const [searchParams] = useSearchParams();
const [isError, setIsError] = useState(false);
const setVerifyEmailNextPath = useSetRecoilState(verifyEmailNextPathState);
const setVerifyEmailRedirectPath = useSetRecoilState(
verifyEmailRedirectPathState,
);
const email = searchParams.get('email');
const emailVerificationToken = searchParams.get('emailVerificationToken');
const verifyEmailNextPath = searchParams.get('nextPath');
const verifyEmailRedirectPath = searchParams.get('nextPath');
const navigate = useNavigateApp();
const { redirectToWorkspaceDomain } = useRedirectToWorkspaceDomain();
const { verifyLoginToken } = useVerifyLogin();
const { isOnAWorkspace } = useIsCurrentLocationOnAWorkspace();
const { t } = useLingui();
useEffect(() => {
@@ -47,19 +54,30 @@ export const VerifyEmailEffect = () => {
return navigate(AppPath.SignInUp);
}
const successSnackbarParams = {
message: t`Email verified.`,
options: {
dedupeKey: 'email-verification-dedupe-key',
},
};
try {
if (!isOnAWorkspace) {
await getWorkspaceAgnosticTokenFromEmailVerificationToken(
emailVerificationToken,
email,
);
return enqueueSuccessSnackBar(successSnackbarParams);
}
const { loginToken, workspaceUrls } =
await getLoginTokenFromEmailVerificationToken(
emailVerificationToken,
email,
);
enqueueSuccessSnackBar({
message: t`Email verified.`,
options: {
dedupeKey: 'email-verification-dedupe-key',
},
});
enqueueSuccessSnackBar(successSnackbarParams);
const workspaceUrl = getWorkspaceUrl(workspaceUrls);
if (workspaceUrl.slice(0, -1) !== window.location.origin) {
@@ -68,11 +86,11 @@ export const VerifyEmailEffect = () => {
});
}
if (isDefined(verifyEmailNextPath)) {
setVerifyEmailNextPath(verifyEmailNextPath);
if (isDefined(verifyEmailRedirectPath)) {
setVerifyEmailRedirectPath(verifyEmailRedirectPath);
}
verifyLoginToken(loginToken.token);
return verifyLoginToken(loginToken.token);
} catch (error) {
enqueueErrorSnackBar({
...(error instanceof ApolloError
@@ -95,7 +113,6 @@ export const VerifyEmailEffect = () => {
};
verifyEmailToken();
// Verify email only needs to run once at mount
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);