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:
@@ -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
|
||||
}, []);
|
||||
|
||||
@@ -7,9 +7,9 @@ export const AUTH_TOKEN = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const AUTH_TOKENS = gql`
|
||||
fragment AuthTokensFragment on AuthTokenPair {
|
||||
accessToken {
|
||||
export const AUTH_TOKEN_PAIR = gql`
|
||||
fragment AuthTokenPairFragment on AuthTokenPair {
|
||||
accessOrWorkspaceAgnosticToken {
|
||||
...AuthTokenFragment
|
||||
}
|
||||
refreshToken {
|
||||
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const GET_AUTH_TOKENS_FROM_LOGIN_TOKEN = gql`
|
||||
mutation GetAuthTokensFromLoginToken($loginToken: String!, $origin: String!) {
|
||||
mutation getAuthTokensFromLoginToken($loginToken: String!, $origin: String!) {
|
||||
getAuthTokensFromLoginToken(loginToken: $loginToken, origin: $origin) {
|
||||
tokens {
|
||||
...AuthTokensFragment
|
||||
...AuthTokenPairFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ export const GET_AUTH_TOKENS_FROM_OTP = gql`
|
||||
origin: $origin
|
||||
) {
|
||||
tokens {
|
||||
...AuthTokensFragment
|
||||
...AuthTokenPairFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const GET_WORKSPACE_AGNOSTIC_TOKEN_FROM_EMAIL_VERIFICATION_TOKEN = gql`
|
||||
mutation GetWorkspaceAgnosticTokenFromEmailVerificationToken(
|
||||
$emailVerificationToken: String!
|
||||
$email: String!
|
||||
$captchaToken: String
|
||||
) {
|
||||
getWorkspaceAgnosticTokenFromEmailVerificationToken(
|
||||
emailVerificationToken: $emailVerificationToken
|
||||
email: $email
|
||||
captchaToken: $captchaToken
|
||||
) {
|
||||
availableWorkspaces {
|
||||
...AvailableWorkspacesFragment
|
||||
}
|
||||
tokens {
|
||||
...AuthTokenPairFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -4,7 +4,7 @@ export const RENEW_TOKEN = gql`
|
||||
mutation RenewToken($appToken: String!) {
|
||||
renewToken(appToken: $appToken) {
|
||||
tokens {
|
||||
...AuthTokensFragment
|
||||
...AuthTokenPairFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ export const SIGN_IN = gql`
|
||||
...AvailableWorkspacesFragment
|
||||
}
|
||||
tokens {
|
||||
...AuthTokensFragment
|
||||
...AuthTokenPairFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const SIGN_UP = gql`
|
||||
mutation SignUp($email: String!, $password: String!, $captchaToken: String) {
|
||||
signUp(email: $email, password: $password, captchaToken: $captchaToken) {
|
||||
mutation SignUp(
|
||||
$email: String!
|
||||
$password: String!
|
||||
$captchaToken: String
|
||||
$locale: String
|
||||
$verifyEmailRedirectPath: String
|
||||
) {
|
||||
signUp(
|
||||
email: $email
|
||||
password: $password
|
||||
captchaToken: $captchaToken
|
||||
locale: $locale
|
||||
verifyEmailRedirectPath: $verifyEmailRedirectPath
|
||||
) {
|
||||
availableWorkspaces {
|
||||
...AvailableWorkspacesFragment
|
||||
}
|
||||
tokens {
|
||||
...AuthTokensFragment
|
||||
...AuthTokenPairFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export const SIGN_UP_IN_WORKSPACE = gql`
|
||||
$captchaToken: String
|
||||
$workspaceId: String
|
||||
$locale: String
|
||||
$verifyEmailNextPath: String
|
||||
$verifyEmailRedirectPath: String
|
||||
) {
|
||||
signUpInWorkspace(
|
||||
email: $email
|
||||
@@ -19,7 +19,7 @@ export const SIGN_UP_IN_WORKSPACE = gql`
|
||||
captchaToken: $captchaToken
|
||||
workspaceId: $workspaceId
|
||||
locale: $locale
|
||||
verifyEmailNextPath: $verifyEmailNextPath
|
||||
verifyEmailRedirectPath: $verifyEmailRedirectPath
|
||||
) {
|
||||
loginToken {
|
||||
...AuthTokenFragment
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
GetAuthTokensFromLoginTokenDocument,
|
||||
GetCurrentUserDocument,
|
||||
GetLoginTokenFromCredentialsDocument,
|
||||
SignUpDocument,
|
||||
SignUpInWorkspaceDocument,
|
||||
GetAuthTokensFromLoginTokenDocument,
|
||||
GetCurrentUserDocument,
|
||||
GetLoginTokenFromCredentialsDocument,
|
||||
SignUpDocument,
|
||||
SignUpInWorkspaceDocument,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
export const queries = {
|
||||
@@ -51,7 +51,7 @@ export const results = {
|
||||
},
|
||||
getAuthTokensFromLoginToken: {
|
||||
tokens: {
|
||||
accessToken: { token, expiresAt: 'expiresAt' },
|
||||
accessOrWorkspaceAgnosticToken: { token, expiresAt: 'expiresAt' },
|
||||
refreshToken: { token, expiresAt: 'expiresAt' },
|
||||
},
|
||||
},
|
||||
|
||||
@@ -30,7 +30,7 @@ describe('useIsLogged', () => {
|
||||
|
||||
await act(async () => {
|
||||
result.current.setTokenPair({
|
||||
accessToken: {
|
||||
accessOrWorkspaceAgnosticToken: {
|
||||
expiresAt: '',
|
||||
token: 'testToken',
|
||||
},
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
useGetCurrentUserLazyQuery,
|
||||
useGetLoginTokenFromCredentialsMutation,
|
||||
useGetLoginTokenFromEmailVerificationTokenMutation,
|
||||
useGetWorkspaceAgnosticTokenFromEmailVerificationTokenMutation,
|
||||
useSignInMutation,
|
||||
useSignUpInWorkspaceMutation,
|
||||
useSignUpMutation,
|
||||
@@ -72,7 +73,7 @@ import { useRefreshObjectMetadataItems } from '@/object-metadata/hooks/useRefres
|
||||
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||
import { i18n } from '@lingui/core';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { APP_LOCALES } from 'twenty-shared/translations';
|
||||
import { APP_LOCALES, SOURCE_LOCALE } from 'twenty-shared/translations';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { iconsState } from 'twenty-ui/display';
|
||||
import { AuthToken } from '~/generated/graphql';
|
||||
@@ -117,6 +118,8 @@ export const useAuth = () => {
|
||||
useGetAuthTokensFromLoginTokenMutation();
|
||||
const [getLoginTokenFromEmailVerificationToken] =
|
||||
useGetLoginTokenFromEmailVerificationTokenMutation();
|
||||
const [getWorkspaceAgnosticTokenFromEmailVerificationToken] =
|
||||
useGetWorkspaceAgnosticTokenFromEmailVerificationTokenMutation();
|
||||
const [getCurrentUser] = useGetCurrentUserLazyQuery();
|
||||
const [getAuthTokensFromOtp] = useGetAuthTokensFromOtpMutation();
|
||||
|
||||
@@ -204,6 +207,111 @@ export const useAuth = () => {
|
||||
[navigate, client, goToRecoilSnapshot, setLastAuthenticateWorkspaceDomain],
|
||||
);
|
||||
|
||||
const loadCurrentUser = useCallback(async () => {
|
||||
const currentUserResult = await getCurrentUser({
|
||||
fetchPolicy: 'network-only',
|
||||
});
|
||||
|
||||
if (isDefined(currentUserResult.error)) {
|
||||
throw new Error(currentUserResult.error.message);
|
||||
}
|
||||
|
||||
const user = currentUserResult.data?.currentUser;
|
||||
|
||||
if (!user) {
|
||||
throw new Error('No current user result');
|
||||
}
|
||||
|
||||
let workspaceMember = null;
|
||||
|
||||
setCurrentUser(user);
|
||||
|
||||
if (isDefined(user.workspaceMembers)) {
|
||||
const workspaceMembers = user.workspaceMembers.map((workspaceMember) => ({
|
||||
...workspaceMember,
|
||||
colorScheme: workspaceMember.colorScheme as ColorScheme,
|
||||
locale: workspaceMember.locale ?? SOURCE_LOCALE,
|
||||
}));
|
||||
|
||||
setCurrentWorkspaceMembers(workspaceMembers);
|
||||
}
|
||||
|
||||
if (isDefined(user.availableWorkspaces)) {
|
||||
setAvailableWorkspaces(user.availableWorkspaces);
|
||||
}
|
||||
|
||||
if (isDefined(user.currentUserWorkspace)) {
|
||||
setCurrentUserWorkspace(user.currentUserWorkspace);
|
||||
}
|
||||
|
||||
if (isDefined(user.workspaceMember)) {
|
||||
workspaceMember = {
|
||||
...user.workspaceMember,
|
||||
colorScheme: user.workspaceMember?.colorScheme as ColorScheme,
|
||||
locale: user.workspaceMember?.locale ?? SOURCE_LOCALE,
|
||||
};
|
||||
|
||||
setCurrentWorkspaceMember(workspaceMember);
|
||||
|
||||
// TODO: factorize with UserProviderEffect
|
||||
setDateTimeFormat({
|
||||
timeZone:
|
||||
workspaceMember.timeZone && workspaceMember.timeZone !== 'system'
|
||||
? workspaceMember.timeZone
|
||||
: detectTimeZone(),
|
||||
dateFormat: isDefined(user.workspaceMember.dateFormat)
|
||||
? getDateFormatFromWorkspaceDateFormat(
|
||||
user.workspaceMember.dateFormat,
|
||||
)
|
||||
: DateFormat[detectDateFormat()],
|
||||
timeFormat: isDefined(user.workspaceMember.timeFormat)
|
||||
? getTimeFormatFromWorkspaceTimeFormat(
|
||||
user.workspaceMember.timeFormat,
|
||||
)
|
||||
: TimeFormat[detectTimeFormat()],
|
||||
});
|
||||
dynamicActivate(
|
||||
(workspaceMember.locale as keyof typeof APP_LOCALES) ?? SOURCE_LOCALE,
|
||||
);
|
||||
}
|
||||
|
||||
const workspace = user.currentWorkspace ?? null;
|
||||
|
||||
setCurrentWorkspace(workspace);
|
||||
|
||||
if (isDefined(workspace) && isOnAWorkspace) {
|
||||
setLastAuthenticateWorkspaceDomain({
|
||||
workspaceId: workspace.id,
|
||||
workspaceUrl: getWorkspaceUrl(workspace.workspaceUrls),
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
workspaceMember,
|
||||
workspace,
|
||||
};
|
||||
}, [
|
||||
getCurrentUser,
|
||||
isOnAWorkspace,
|
||||
setCurrentUser,
|
||||
setCurrentUserWorkspace,
|
||||
setCurrentWorkspace,
|
||||
setCurrentWorkspaceMember,
|
||||
setCurrentWorkspaceMembers,
|
||||
setDateTimeFormat,
|
||||
setLastAuthenticateWorkspaceDomain,
|
||||
setAvailableWorkspaces,
|
||||
]);
|
||||
|
||||
const handleSetAuthTokens = useCallback(
|
||||
(tokens: AuthTokenPair) => {
|
||||
setTokenPair(tokens);
|
||||
cookieStorage.setItem('tokenPair', JSON.stringify(tokens));
|
||||
},
|
||||
[setTokenPair],
|
||||
);
|
||||
|
||||
const handleGetLoginTokenFromCredentials = useCallback(
|
||||
async (email: string, password: string, captchaToken?: string) => {
|
||||
try {
|
||||
@@ -268,109 +376,48 @@ export const useAuth = () => {
|
||||
[getLoginTokenFromEmailVerificationToken, origin],
|
||||
);
|
||||
|
||||
const loadCurrentUser = useCallback(async () => {
|
||||
const currentUserResult = await getCurrentUser({
|
||||
fetchPolicy: 'network-only',
|
||||
});
|
||||
const handleGetWorkspaceAgnosticTokenFromEmailVerificationToken = useCallback(
|
||||
async (
|
||||
emailVerificationToken: string,
|
||||
email: string,
|
||||
captchaToken?: string,
|
||||
) => {
|
||||
const { data, errors } =
|
||||
await getWorkspaceAgnosticTokenFromEmailVerificationToken({
|
||||
variables: {
|
||||
email,
|
||||
emailVerificationToken,
|
||||
captchaToken,
|
||||
},
|
||||
});
|
||||
|
||||
if (isDefined(currentUserResult.error)) {
|
||||
throw new Error(currentUserResult.error.message);
|
||||
}
|
||||
if (isDefined(errors)) {
|
||||
throw errors;
|
||||
}
|
||||
|
||||
const user = currentUserResult.data?.currentUser;
|
||||
if (!data?.getWorkspaceAgnosticTokenFromEmailVerificationToken) {
|
||||
throw new Error('No workspace agnostic token in result');
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
throw new Error('No current user result');
|
||||
}
|
||||
|
||||
let workspaceMember = null;
|
||||
|
||||
setCurrentUser(user);
|
||||
|
||||
if (isDefined(user.workspaceMembers)) {
|
||||
const workspaceMembers = user.workspaceMembers.map((workspaceMember) => ({
|
||||
...workspaceMember,
|
||||
colorScheme: workspaceMember.colorScheme as ColorScheme,
|
||||
locale: workspaceMember.locale ?? 'en',
|
||||
}));
|
||||
|
||||
setCurrentWorkspaceMembers(workspaceMembers);
|
||||
}
|
||||
|
||||
if (isDefined(user.availableWorkspaces)) {
|
||||
setAvailableWorkspaces(user.availableWorkspaces);
|
||||
}
|
||||
|
||||
if (isDefined(user.currentUserWorkspace)) {
|
||||
setCurrentUserWorkspace(user.currentUserWorkspace);
|
||||
}
|
||||
|
||||
if (isDefined(user.workspaceMember)) {
|
||||
workspaceMember = {
|
||||
...user.workspaceMember,
|
||||
colorScheme: user.workspaceMember?.colorScheme as ColorScheme,
|
||||
locale: user.workspaceMember?.locale ?? 'en',
|
||||
};
|
||||
|
||||
setCurrentWorkspaceMember(workspaceMember);
|
||||
|
||||
// TODO: factorize with UserProviderEffect
|
||||
setDateTimeFormat({
|
||||
timeZone:
|
||||
workspaceMember.timeZone && workspaceMember.timeZone !== 'system'
|
||||
? workspaceMember.timeZone
|
||||
: detectTimeZone(),
|
||||
dateFormat: isDefined(user.workspaceMember.dateFormat)
|
||||
? getDateFormatFromWorkspaceDateFormat(
|
||||
user.workspaceMember.dateFormat,
|
||||
)
|
||||
: DateFormat[detectDateFormat()],
|
||||
timeFormat: isDefined(user.workspaceMember.timeFormat)
|
||||
? getTimeFormatFromWorkspaceTimeFormat(
|
||||
user.workspaceMember.timeFormat,
|
||||
)
|
||||
: TimeFormat[detectTimeFormat()],
|
||||
});
|
||||
dynamicActivate(
|
||||
(workspaceMember.locale as keyof typeof APP_LOCALES) ?? 'en',
|
||||
handleSetAuthTokens(
|
||||
data.getWorkspaceAgnosticTokenFromEmailVerificationToken.tokens,
|
||||
);
|
||||
}
|
||||
|
||||
const workspace = user.currentWorkspace ?? null;
|
||||
const { user } = await loadCurrentUser();
|
||||
|
||||
setCurrentWorkspace(workspace);
|
||||
if (countAvailableWorkspaces(user.availableWorkspaces) === 0) {
|
||||
return await createWorkspace({ newTab: false });
|
||||
}
|
||||
|
||||
if (isDefined(workspace) && isOnAWorkspace) {
|
||||
setLastAuthenticateWorkspaceDomain({
|
||||
workspaceId: workspace.id,
|
||||
workspaceUrl: getWorkspaceUrl(workspace.workspaceUrls),
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
workspaceMember,
|
||||
workspace,
|
||||
};
|
||||
}, [
|
||||
getCurrentUser,
|
||||
isOnAWorkspace,
|
||||
setCurrentUser,
|
||||
setCurrentUserWorkspace,
|
||||
setCurrentWorkspace,
|
||||
setCurrentWorkspaceMember,
|
||||
setCurrentWorkspaceMembers,
|
||||
setDateTimeFormat,
|
||||
setLastAuthenticateWorkspaceDomain,
|
||||
setAvailableWorkspaces,
|
||||
]);
|
||||
|
||||
const handleSetAuthTokens = useCallback(
|
||||
(tokens: AuthTokenPair) => {
|
||||
setTokenPair(tokens);
|
||||
cookieStorage.setItem('tokenPair', JSON.stringify(tokens));
|
||||
setSignInUpStep(SignInUpStep.WorkspaceSelection);
|
||||
},
|
||||
[setTokenPair],
|
||||
[
|
||||
createWorkspace,
|
||||
getWorkspaceAgnosticTokenFromEmailVerificationToken,
|
||||
handleSetAuthTokens,
|
||||
loadCurrentUser,
|
||||
setSignInUpStep,
|
||||
],
|
||||
);
|
||||
|
||||
const handleSetLoginToken = useCallback(
|
||||
@@ -507,13 +554,24 @@ export const useAuth = () => {
|
||||
const handleCredentialsSignUp = useCallback(
|
||||
async (email: string, password: string, captchaToken?: string) => {
|
||||
const signUpResult = await signUp({
|
||||
variables: { email, password, captchaToken },
|
||||
variables: {
|
||||
email,
|
||||
password,
|
||||
captchaToken,
|
||||
locale: i18n.locale ?? SOURCE_LOCALE,
|
||||
},
|
||||
});
|
||||
|
||||
if (isDefined(signUpResult.errors)) {
|
||||
throw signUpResult.errors;
|
||||
}
|
||||
|
||||
if (isEmailVerificationRequired) {
|
||||
setSearchParams({ email });
|
||||
setSignInUpStep(SignInUpStep.EmailVerification);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!signUpResult.data?.signUp) {
|
||||
throw new Error('No signUp result');
|
||||
}
|
||||
@@ -529,6 +587,8 @@ export const useAuth = () => {
|
||||
setSignInUpStep(SignInUpStep.WorkspaceSelection);
|
||||
},
|
||||
[
|
||||
isEmailVerificationRequired,
|
||||
setSearchParams,
|
||||
handleSetAuthTokens,
|
||||
signUp,
|
||||
loadCurrentUser,
|
||||
@@ -561,14 +621,14 @@ export const useAuth = () => {
|
||||
workspaceInviteHash,
|
||||
workspacePersonalInviteToken,
|
||||
captchaToken,
|
||||
verifyEmailNextPath,
|
||||
verifyEmailRedirectPath,
|
||||
}: {
|
||||
email: string;
|
||||
password: string;
|
||||
workspaceInviteHash?: string;
|
||||
workspacePersonalInviteToken?: string;
|
||||
captchaToken?: string;
|
||||
verifyEmailNextPath?: string;
|
||||
verifyEmailRedirectPath?: string;
|
||||
}) => {
|
||||
const signUpInWorkspaceResult = await signUpInWorkspace({
|
||||
variables: {
|
||||
@@ -577,11 +637,11 @@ export const useAuth = () => {
|
||||
workspaceInviteHash,
|
||||
workspacePersonalInviteToken,
|
||||
captchaToken,
|
||||
locale: i18n.locale ?? 'en',
|
||||
locale: i18n.locale ?? SOURCE_LOCALE,
|
||||
...(workspacePublicData?.id
|
||||
? { workspaceId: workspacePublicData.id }
|
||||
: {}),
|
||||
verifyEmailNextPath,
|
||||
verifyEmailRedirectPath,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -712,7 +772,7 @@ export const useAuth = () => {
|
||||
}
|
||||
|
||||
if (!getAuthTokensFromOtpResult.data?.getAuthTokensFromOTP) {
|
||||
throw new Error('No getAuthTokensFromLoginToken result');
|
||||
throw new Error('No getAuthTokensFromOTP result');
|
||||
}
|
||||
|
||||
await handleLoadWorkspaceAfterAuthentication(
|
||||
@@ -724,6 +784,8 @@ export const useAuth = () => {
|
||||
|
||||
return {
|
||||
getLoginTokenFromCredentials: handleGetLoginTokenFromCredentials,
|
||||
getWorkspaceAgnosticTokenFromEmailVerificationToken:
|
||||
handleGetWorkspaceAgnosticTokenFromEmailVerificationToken,
|
||||
getLoginTokenFromEmailVerificationToken:
|
||||
handleGetLoginTokenFromEmailVerificationToken,
|
||||
getAuthTokensFromLoginToken: handleGetAuthTokensFromLoginToken,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { renewToken } from '@/auth/services/AuthService';
|
||||
enableFetchMocks();
|
||||
|
||||
const tokens = {
|
||||
accessToken: {
|
||||
accessOrWorkspaceAgnosticToken: {
|
||||
token: 'accessToken',
|
||||
expiresAt: 'expiresAt',
|
||||
},
|
||||
|
||||
@@ -130,7 +130,7 @@ export const useSignInUp = (form: UseFormReturn<Form>) => {
|
||||
);
|
||||
}
|
||||
|
||||
const verifyEmailNextPath = buildAppPathWithQueryParams(
|
||||
const verifyEmailRedirectPath = buildAppPathWithQueryParams(
|
||||
AppPath.PlanRequired,
|
||||
await buildSearchParamsFromUrlSyncedStates(),
|
||||
);
|
||||
@@ -141,7 +141,7 @@ export const useSignInUp = (form: UseFormReturn<Form>) => {
|
||||
workspaceInviteHash,
|
||||
workspacePersonalInviteToken,
|
||||
captchaToken: token,
|
||||
verifyEmailNextPath,
|
||||
verifyEmailRedirectPath,
|
||||
});
|
||||
} catch (error: any) {
|
||||
enqueueErrorSnackBar({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createState } from 'twenty-ui/utilities';
|
||||
import { AuthTokenPair } from '~/generated/graphql';
|
||||
import { cookieStorageEffect } from '~/utils/recoil-effects';
|
||||
import { createState } from 'twenty-ui/utilities';
|
||||
|
||||
export const tokenPairState = createState<AuthTokenPair | null>({
|
||||
key: 'tokenPairState',
|
||||
@@ -11,7 +11,7 @@ export const tokenPairState = createState<AuthTokenPair | null>({
|
||||
{},
|
||||
{
|
||||
validateInitFn: (payload: AuthTokenPair) =>
|
||||
Boolean(payload['accessToken']),
|
||||
Boolean(payload['accessOrWorkspaceAgnosticToken']),
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user