fix(auth): add captcha auto-refresh via ApolloLink (#12758)
- Introduced `createCaptchaRefreshLink` to trigger captcha token refresh automatically. - Removed redundant manual captcha refresh calls and integrated it into Apollo Provider.
This commit is contained in:
@ -1,10 +1,17 @@
|
|||||||
import { ApolloProvider as ApolloProviderBase } from '@apollo/client';
|
import { ApolloProvider as ApolloProviderBase } from '@apollo/client';
|
||||||
|
|
||||||
import { useApolloFactory } from '@/apollo/hooks/useApolloFactory';
|
import { useApolloFactory } from '@/apollo/hooks/useApolloFactory';
|
||||||
|
import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken';
|
||||||
|
import { createCaptchaRefreshLink } from '@/apollo/utils/captchaRefreshLink';
|
||||||
|
|
||||||
export const ApolloProvider = ({ children }: React.PropsWithChildren) => {
|
export const ApolloProvider = ({ children }: React.PropsWithChildren) => {
|
||||||
|
const { requestFreshCaptchaToken } = useRequestFreshCaptchaToken();
|
||||||
|
|
||||||
|
const captchaRefreshLink = createCaptchaRefreshLink(requestFreshCaptchaToken);
|
||||||
|
|
||||||
const apolloClient = useApolloFactory({
|
const apolloClient = useApolloFactory({
|
||||||
connectToDevTools: true,
|
connectToDevTools: true,
|
||||||
|
extraLinks: [captchaRefreshLink],
|
||||||
});
|
});
|
||||||
|
|
||||||
// This will attach the right apollo client to Apollo Dev Tools
|
// This will attach the right apollo client to Apollo Dev Tools
|
||||||
|
|||||||
@ -0,0 +1,19 @@
|
|||||||
|
import { ApolloLink } from '@apollo/client';
|
||||||
|
|
||||||
|
export const createCaptchaRefreshLink = (
|
||||||
|
requestFreshCaptchaToken: () => void,
|
||||||
|
) => {
|
||||||
|
return new ApolloLink((operation, forward) => {
|
||||||
|
const { variables } = operation;
|
||||||
|
|
||||||
|
const hasCaptchaToken = variables && 'captchaToken' in variables;
|
||||||
|
|
||||||
|
return forward(operation).map((response) => {
|
||||||
|
if (hasCaptchaToken) {
|
||||||
|
requestFreshCaptchaToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -501,7 +501,8 @@ export const useAuth = () => {
|
|||||||
|
|
||||||
const handleSignOut = useCallback(async () => {
|
const handleSignOut = useCallback(async () => {
|
||||||
await clearSession();
|
await clearSession();
|
||||||
}, [clearSession]);
|
await requestFreshCaptchaToken();
|
||||||
|
}, [clearSession, requestFreshCaptchaToken]);
|
||||||
|
|
||||||
const handleCredentialsSignUpInWorkspace = useCallback(
|
const handleCredentialsSignUpInWorkspace = useCallback(
|
||||||
async ({
|
async ({
|
||||||
@ -519,60 +520,55 @@ export const useAuth = () => {
|
|||||||
captchaToken?: string;
|
captchaToken?: string;
|
||||||
verifyEmailNextPath?: string;
|
verifyEmailNextPath?: string;
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
const signUpInWorkspaceResult = await signUpInWorkspace({
|
||||||
const signUpInWorkspaceResult = await signUpInWorkspace({
|
variables: {
|
||||||
variables: {
|
email,
|
||||||
email,
|
password,
|
||||||
password,
|
workspaceInviteHash,
|
||||||
workspaceInviteHash,
|
workspacePersonalInviteToken,
|
||||||
workspacePersonalInviteToken,
|
captchaToken,
|
||||||
captchaToken,
|
locale: i18n.locale ?? 'en',
|
||||||
locale: i18n.locale ?? 'en',
|
...(workspacePublicData?.id
|
||||||
...(workspacePublicData?.id
|
? { workspaceId: workspacePublicData.id }
|
||||||
? { workspaceId: workspacePublicData.id }
|
: {}),
|
||||||
: {}),
|
verifyEmailNextPath,
|
||||||
verifyEmailNextPath,
|
},
|
||||||
},
|
});
|
||||||
});
|
|
||||||
|
|
||||||
if (isDefined(signUpInWorkspaceResult.errors)) {
|
if (isDefined(signUpInWorkspaceResult.errors)) {
|
||||||
throw signUpInWorkspaceResult.errors;
|
throw signUpInWorkspaceResult.errors;
|
||||||
}
|
|
||||||
|
|
||||||
if (!signUpInWorkspaceResult.data?.signUpInWorkspace) {
|
|
||||||
throw new Error('No login token');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEmailVerificationRequired) {
|
|
||||||
setSearchParams({ email });
|
|
||||||
setSignInUpStep(SignInUpStep.EmailVerification);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isMultiWorkspaceEnabled) {
|
|
||||||
return await redirectToWorkspaceDomain(
|
|
||||||
getWorkspaceUrl(
|
|
||||||
signUpInWorkspaceResult.data.signUpInWorkspace.workspace
|
|
||||||
.workspaceUrls,
|
|
||||||
),
|
|
||||||
isEmailVerificationRequired ? AppPath.SignInUp : AppPath.Verify,
|
|
||||||
{
|
|
||||||
...(!isEmailVerificationRequired && {
|
|
||||||
loginToken:
|
|
||||||
signUpInWorkspaceResult.data.signUpInWorkspace.loginToken
|
|
||||||
.token,
|
|
||||||
}),
|
|
||||||
email,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await handleGetAuthTokensFromLoginToken(
|
|
||||||
signUpInWorkspaceResult.data?.signUpInWorkspace.loginToken.token,
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
requestFreshCaptchaToken();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!signUpInWorkspaceResult.data?.signUpInWorkspace) {
|
||||||
|
throw new Error('No login token');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEmailVerificationRequired) {
|
||||||
|
setSearchParams({ email });
|
||||||
|
setSignInUpStep(SignInUpStep.EmailVerification);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMultiWorkspaceEnabled) {
|
||||||
|
return await redirectToWorkspaceDomain(
|
||||||
|
getWorkspaceUrl(
|
||||||
|
signUpInWorkspaceResult.data.signUpInWorkspace.workspace
|
||||||
|
.workspaceUrls,
|
||||||
|
),
|
||||||
|
isEmailVerificationRequired ? AppPath.SignInUp : AppPath.Verify,
|
||||||
|
{
|
||||||
|
...(!isEmailVerificationRequired && {
|
||||||
|
loginToken:
|
||||||
|
signUpInWorkspaceResult.data.signUpInWorkspace.loginToken.token,
|
||||||
|
}),
|
||||||
|
email,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await handleGetAuthTokensFromLoginToken(
|
||||||
|
signUpInWorkspaceResult.data?.signUpInWorkspace.loginToken.token,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
signUpInWorkspace,
|
signUpInWorkspace,
|
||||||
@ -583,7 +579,6 @@ export const useAuth = () => {
|
|||||||
setSearchParams,
|
setSearchParams,
|
||||||
isEmailVerificationRequired,
|
isEmailVerificationRequired,
|
||||||
redirectToWorkspaceDomain,
|
redirectToWorkspaceDomain,
|
||||||
requestFreshCaptchaToken,
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import {
|
|||||||
} from '@/auth/states/signInUpStepState';
|
} from '@/auth/states/signInUpStepState';
|
||||||
import { SignInUpMode } from '@/auth/types/signInUpMode';
|
import { SignInUpMode } from '@/auth/types/signInUpMode';
|
||||||
import { useReadCaptchaToken } from '@/captcha/hooks/useReadCaptchaToken';
|
import { useReadCaptchaToken } from '@/captcha/hooks/useReadCaptchaToken';
|
||||||
import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken';
|
|
||||||
import { useBuildSearchParamsFromUrlSyncedStates } from '@/domain-manager/hooks/useBuildSearchParamsFromUrlSyncedStates';
|
import { useBuildSearchParamsFromUrlSyncedStates } from '@/domain-manager/hooks/useBuildSearchParamsFromUrlSyncedStates';
|
||||||
import { AppPath } from '@/types/AppPath';
|
import { AppPath } from '@/types/AppPath';
|
||||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||||
@ -47,16 +46,14 @@ export const useSignInUp = (form: UseFormReturn<Form>) => {
|
|||||||
checkUserExists: { checkUserExistsQuery },
|
checkUserExists: { checkUserExistsQuery },
|
||||||
} = useAuth();
|
} = useAuth();
|
||||||
|
|
||||||
const { requestFreshCaptchaToken } = useRequestFreshCaptchaToken();
|
|
||||||
const { readCaptchaToken } = useReadCaptchaToken();
|
const { readCaptchaToken } = useReadCaptchaToken();
|
||||||
|
|
||||||
const { buildSearchParamsFromUrlSyncedStates } =
|
const { buildSearchParamsFromUrlSyncedStates } =
|
||||||
useBuildSearchParamsFromUrlSyncedStates();
|
useBuildSearchParamsFromUrlSyncedStates();
|
||||||
|
|
||||||
const continueWithEmail = useCallback(() => {
|
const continueWithEmail = useCallback(() => {
|
||||||
requestFreshCaptchaToken();
|
|
||||||
setSignInUpStep(SignInUpStep.Email);
|
setSignInUpStep(SignInUpStep.Email);
|
||||||
}, [requestFreshCaptchaToken, setSignInUpStep]);
|
}, [setSignInUpStep]);
|
||||||
|
|
||||||
const continueWithCredentials = useCallback(async () => {
|
const continueWithCredentials = useCallback(async () => {
|
||||||
const token = await readCaptchaToken();
|
const token = await readCaptchaToken();
|
||||||
@ -74,7 +71,6 @@ export const useSignInUp = (form: UseFormReturn<Form>) => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
onCompleted: (data) => {
|
onCompleted: (data) => {
|
||||||
requestFreshCaptchaToken();
|
|
||||||
setSignInUpMode(
|
setSignInUpMode(
|
||||||
data?.checkUserExists.exists
|
data?.checkUserExists.exists
|
||||||
? SignInUpMode.SignIn
|
? SignInUpMode.SignIn
|
||||||
@ -88,7 +84,6 @@ export const useSignInUp = (form: UseFormReturn<Form>) => {
|
|||||||
form,
|
form,
|
||||||
checkUserExistsQuery,
|
checkUserExistsQuery,
|
||||||
enqueueSnackBar,
|
enqueueSnackBar,
|
||||||
requestFreshCaptchaToken,
|
|
||||||
setSignInUpStep,
|
setSignInUpStep,
|
||||||
setSignInUpMode,
|
setSignInUpMode,
|
||||||
]);
|
]);
|
||||||
@ -154,8 +149,6 @@ export const useSignInUp = (form: UseFormReturn<Form>) => {
|
|||||||
enqueueSnackBar(err?.message, {
|
enqueueSnackBar(err?.message, {
|
||||||
variant: SnackBarVariant.Error,
|
variant: SnackBarVariant.Error,
|
||||||
});
|
});
|
||||||
} finally {
|
|
||||||
requestFreshCaptchaToken();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
@ -169,7 +162,6 @@ export const useSignInUp = (form: UseFormReturn<Form>) => {
|
|||||||
workspaceInviteHash,
|
workspaceInviteHash,
|
||||||
workspacePersonalInviteToken,
|
workspacePersonalInviteToken,
|
||||||
enqueueSnackBar,
|
enqueueSnackBar,
|
||||||
requestFreshCaptchaToken,
|
|
||||||
buildSearchParamsFromUrlSyncedStates,
|
buildSearchParamsFromUrlSyncedStates,
|
||||||
isOnAWorkspace,
|
isOnAWorkspace,
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user