Rename Unintuitive Function Names in Authentication Flow (#9706)

Resolves #9623

## Description

This PR renames the following functions to better reflect their purpose.

- Backend:
  - Verify → GetAuthTokensFromLoginToken
  - Challenge → GetLoginTokenFromCredentials

- Frontend:
  - challenge → getLoginTokenFromCredentials
  - verify → getAuthTokensFromLoginToken

## Testing
_Sign in works as expected:_


https://github.com/user-attachments/assets/7e8f73c7-2c7d-4cd2-9965-5ad9f5334cd3

_Sign up works as expected:_
  

https://github.com/user-attachments/assets/d1794ee4-8b59-4934-84df-d819eabd5224

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Samyak Piya
2025-01-24 13:19:14 -05:00
committed by GitHub
parent 570b2e3530
commit 55be726105
19 changed files with 300 additions and 259 deletions

View File

@ -21,7 +21,7 @@ export const VerifyEffect = () => {
const isLogged = useIsLogged();
const navigate = useNavigateApp();
const { verify } = useAuth();
const { getAuthTokensFromLoginToken } = useAuth();
const setIsAppWaitingForFreshObjectMetadata = useSetRecoilState(
isAppWaitingForFreshObjectMetadataState,
@ -30,14 +30,14 @@ export const VerifyEffect = () => {
useEffect(() => {
if (isDefined(errorMessage)) {
enqueueSnackBar(errorMessage, {
dedupeKey: 'verify-failed-dedupe-key',
dedupeKey: 'get-auth-tokens-from-login-token-failed-dedupe-key',
variant: SnackBarVariant.Error,
});
}
if (isDefined(loginToken)) {
setIsAppWaitingForFreshObjectMetadata(true);
verify(loginToken);
getAuthTokensFromLoginToken(loginToken);
} else if (!isLogged) {
navigate(AppPath.SignInUp);
}

View File

@ -1,15 +0,0 @@
import { gql } from '@apollo/client';
export const CHALLENGE = gql`
mutation Challenge(
$email: String!
$password: String!
$captchaToken: String
) {
challenge(email: $email, password: $password, captchaToken: $captchaToken) {
loginToken {
...AuthTokenFragment
}
}
}
`;

View File

@ -0,0 +1,11 @@
import { gql } from '@apollo/client';
export const GET_AUTH_TOKENS_FROM_LOGIN_TOKEN = gql`
mutation GetAuthTokensFromLoginToken($loginToken: String!) {
getAuthTokensFromLoginToken(loginToken: $loginToken) {
tokens {
...AuthTokensFragment
}
}
}
`;

View File

@ -0,0 +1,19 @@
import { gql } from '@apollo/client';
export const GET_LOGIN_TOKEN_FROM_CREDENTIALS = gql`
mutation GetLoginTokenFromCredentials(
$email: String!
$password: String!
$captchaToken: String
) {
getLoginTokenFromCredentials(
email: $email
password: $password
captchaToken: $captchaToken
) {
loginToken {
...AuthTokenFragment
}
}
}
`;

View File

@ -1,11 +0,0 @@
import { gql } from '@apollo/client';
export const VERIFY = gql`
mutation Verify($loginToken: String!) {
verify(loginToken: $loginToken) {
tokens {
...AuthTokensFragment
}
}
}
`;

View File

@ -1,13 +1,13 @@
import {
ChallengeDocument,
GetAuthTokensFromLoginTokenDocument,
GetCurrentUserDocument,
GetLoginTokenFromCredentialsDocument,
SignUpDocument,
VerifyDocument,
} from '~/generated/graphql';
export const queries = {
challenge: ChallengeDocument,
verify: VerifyDocument,
getLoginTokenFromCredentials: GetLoginTokenFromCredentialsDocument,
getAuthTokensFromLoginToken: GetAuthTokensFromLoginTokenDocument,
signup: SignUpDocument,
getCurrentUser: GetCurrentUserDocument,
};
@ -18,23 +18,23 @@ export const token =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
export const variables = {
challenge: {
getLoginTokenFromCredentials: {
email,
password,
},
verify: { loginToken: token },
getAuthTokensFromLoginToken: { loginToken: token },
signup: {},
getCurrentUser: {},
};
export const results = {
challenge: {
getLoginTokenFromCredentials: {
loginToken: {
token,
expiresAt: '2022-01-01',
},
},
verify: {
getAuthTokensFromLoginToken: {
tokens: {
accessToken: { token, expiresAt: 'expiresAt' },
refreshToken: { token, expiresAt: 'expiresAt' },
@ -81,30 +81,30 @@ export const results = {
export const mocks = [
{
request: {
query: queries.challenge,
variables: variables.challenge,
query: queries.getLoginTokenFromCredentials,
variables: variables.getLoginTokenFromCredentials,
},
result: jest.fn(() => ({
data: {
challenge: results.challenge,
getLoginTokenFromCredentials: results.getLoginTokenFromCredentials,
},
})),
},
{
request: {
query: queries.verify,
variables: variables.verify,
query: queries.getAuthTokensFromLoginToken,
variables: variables.getAuthTokensFromLoginToken,
},
result: jest.fn(() => ({
data: {
verify: results.verify,
getAuthTokensFromLoginToken: results.getAuthTokensFromLoginToken,
},
})),
},
{
request: {
query: queries.signup,
variables: variables.challenge,
variables: variables.getLoginTokenFromCredentials,
},
result: jest.fn(() => ({
data: {

View File

@ -42,13 +42,13 @@ describe('useAuth', () => {
jest.clearAllMocks();
});
it('should return challenge object', async () => {
it('should return login token object', async () => {
const { result } = renderHooks();
await act(async () => {
expect(await result.current.challenge(email, password)).toStrictEqual(
results.challenge,
);
expect(
await result.current.getLoginTokenFromCredentials(email, password),
).toStrictEqual(results.getLoginTokenFromCredentials);
});
expect(mocks[0].result).toHaveBeenCalled();
@ -58,7 +58,7 @@ describe('useAuth', () => {
const { result } = renderHooks();
await act(async () => {
await result.current.verify(token);
await result.current.getAuthTokensFromLoginToken(token);
});
expect(mocks[1].result).toHaveBeenCalled();

View File

@ -22,12 +22,12 @@ import { supportChatState } from '@/client-config/states/supportChatState';
import { ColorScheme } from '@/workspace-member/types/WorkspaceMember';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import {
useChallengeMutation,
useCheckUserExistsLazyQuery,
useGetAuthTokensFromLoginTokenMutation,
useGetCurrentUserLazyQuery,
useGetLoginTokenFromCredentialsMutation,
useGetLoginTokenFromEmailVerificationTokenMutation,
useSignUpMutation,
useVerifyMutation,
} from '~/generated/graphql';
import { isDefined } from '~/utils/isDefined';
@ -87,9 +87,11 @@ export const useAuth = () => {
const { redirect } = useRedirect();
const { redirectToWorkspaceDomain } = useRedirectToWorkspaceDomain();
const [challenge] = useChallengeMutation();
const [getLoginTokenFromCredentials] =
useGetLoginTokenFromCredentialsMutation();
const [signUp] = useSignUpMutation();
const [verify] = useVerifyMutation();
const [getAuthTokensFromLoginToken] =
useGetAuthTokensFromLoginTokenMutation();
const [getLoginTokenFromEmailVerificationToken] =
useGetLoginTokenFromEmailVerificationTokenMutation();
const [getCurrentUser] = useGetCurrentUserLazyQuery();
@ -166,25 +168,25 @@ export const useAuth = () => {
[client, goToRecoilSnapshot, setLastAuthenticateWorkspaceDomain],
);
const handleChallenge = useCallback(
const handleGetLoginTokenFromCredentials = useCallback(
async (email: string, password: string, captchaToken?: string) => {
try {
const challengeResult = await challenge({
const getLoginTokenResult = await getLoginTokenFromCredentials({
variables: {
email,
password,
captchaToken,
},
});
if (isDefined(challengeResult.errors)) {
throw challengeResult.errors;
if (isDefined(getLoginTokenResult.errors)) {
throw getLoginTokenResult.errors;
}
if (!challengeResult.data?.challenge) {
if (!getLoginTokenResult.data?.getLoginTokenFromCredentials) {
throw new Error('No login token');
}
return challengeResult.data.challenge;
return getLoginTokenResult.data.getLoginTokenFromCredentials;
} catch (error) {
// TODO: Get intellisense for graphql error extensions code (codegen?)
if (
@ -198,7 +200,7 @@ export const useAuth = () => {
throw error;
}
},
[challenge, setSearchParams, setSignInUpStep],
[getLoginTokenFromCredentials, setSearchParams, setSignInUpStep],
);
const handleGetLoginTokenFromEmailVerificationToken = useCallback(
@ -322,41 +324,48 @@ export const useAuth = () => {
setWorkspaces,
]);
const handleVerify = useCallback(
const handleGetAuthTokensFromLoginToken = useCallback(
async (loginToken: string) => {
setIsVerifyPendingState(true);
const verifyResult = await verify({
const getAuthTokensResult = await getAuthTokensFromLoginToken({
variables: { loginToken },
});
if (isDefined(verifyResult.errors)) {
throw verifyResult.errors;
if (isDefined(getAuthTokensResult.errors)) {
throw getAuthTokensResult.errors;
}
if (!verifyResult.data?.verify) {
throw new Error('No verify result');
if (!getAuthTokensResult.data?.getAuthTokensFromLoginToken) {
throw new Error('No getAuthTokensFromLoginToken result');
}
setTokenPair(verifyResult.data?.verify.tokens);
setTokenPair(
getAuthTokensResult.data?.getAuthTokensFromLoginToken.tokens,
);
await loadCurrentUser();
setIsVerifyPendingState(false);
},
[setIsVerifyPendingState, verify, setTokenPair, loadCurrentUser],
[
setIsVerifyPendingState,
getAuthTokensFromLoginToken,
setTokenPair,
loadCurrentUser,
],
);
const handleCredentialsSignIn = useCallback(
async (email: string, password: string, captchaToken?: string) => {
const { loginToken } = await handleChallenge(
const { loginToken } = await handleGetLoginTokenFromCredentials(
email,
password,
captchaToken,
);
await handleVerify(loginToken.token);
await handleGetAuthTokensFromLoginToken(loginToken.token);
},
[handleChallenge, handleVerify],
[handleGetLoginTokenFromCredentials, handleGetAuthTokensFromLoginToken],
);
const handleSignOut = useCallback(async () => {
@ -413,14 +422,16 @@ export const useAuth = () => {
);
}
await handleVerify(signUpResult.data?.signUp.loginToken.token);
await handleGetAuthTokensFromLoginToken(
signUpResult.data?.signUp.loginToken.token,
);
},
[
setIsVerifyPendingState,
signUp,
workspacePublicData,
isMultiWorkspaceEnabled,
handleVerify,
handleGetAuthTokensFromLoginToken,
setSignInUpStep,
setSearchParams,
isEmailVerificationRequired,
@ -486,10 +497,10 @@ export const useAuth = () => {
);
return {
challenge: handleChallenge,
getLoginTokenFromCredentials: handleGetLoginTokenFromCredentials,
getLoginTokenFromEmailVerificationToken:
handleGetLoginTokenFromEmailVerificationToken,
verify: handleVerify,
getAuthTokensFromLoginToken: handleGetAuthTokensFromLoginToken,
loadCurrentUser,

View File

@ -1,13 +1,13 @@
import { useAuth } from '@/auth/hooks/useAuth';
import { currentUserState } from '@/auth/states/currentUserState';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
import { isAppWaitingForFreshObjectMetadataState } from '@/object-metadata/states/isAppWaitingForFreshObjectMetadataState';
import { AppPath } from '@/types/AppPath';
import { useState } from 'react';
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
import { useImpersonateMutation } from '~/generated/graphql';
import { isDefined } from '~/utils/isDefined';
import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
import { useAuth } from '@/auth/hooks/useAuth';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { isAppWaitingForFreshObjectMetadataState } from '@/object-metadata/states/isAppWaitingForFreshObjectMetadataState';
export const useImpersonate = () => {
const [currentUser] = useRecoilState(currentUserState);
@ -16,7 +16,7 @@ export const useImpersonate = () => {
isAppWaitingForFreshObjectMetadataState,
);
const { verify } = useAuth();
const { getAuthTokensFromLoginToken } = useAuth();
const [impersonate] = useImpersonateMutation();
const { redirectToWorkspaceDomain } = useRedirectToWorkspaceDomain();
@ -50,7 +50,7 @@ export const useImpersonate = () => {
if (workspace.id === currentWorkspace?.id) {
setIsAppWaitingForFreshObjectMetadata(true);
await verify(loginToken.token);
await getAuthTokensFromLoginToken(loginToken.token);
setIsAppWaitingForFreshObjectMetadata(false);
return;
}