From 53af6448fd20c90629a85b7158f9679b272737c5 Mon Sep 17 00:00:00 2001 From: Florent Lagrede Date: Tue, 31 Oct 2023 09:51:15 +0100 Subject: [PATCH] fix: csrf token refetch (#2196) --- web-marketplace/src/apollo.tsx | 68 ++++++++++++++++--- .../getCsrfTokenQuery.constants.ts | 1 + .../getCsrfTokenQuery/getCsrfTokenQuery.ts | 3 +- 3 files changed, 62 insertions(+), 10 deletions(-) create mode 100644 web-marketplace/src/lib/queries/react-query/registry-server/getCsrfTokenQuery/getCsrfTokenQuery.constants.ts diff --git a/web-marketplace/src/apollo.tsx b/web-marketplace/src/apollo.tsx index 30720326b1..decac6afd4 100644 --- a/web-marketplace/src/apollo.tsx +++ b/web-marketplace/src/apollo.tsx @@ -3,14 +3,19 @@ import { ApolloProvider, createHttpLink, DefaultOptions, + FetchResult, InMemoryCache, + Observable, split, } from '@apollo/client'; import { setContext } from '@apollo/client/link/context'; -import { useQuery } from '@tanstack/react-query'; +import { onError } from '@apollo/client/link/error'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; import { ApolloClientFactory } from 'lib/clients/apolloClientFactory'; import { getCsrfTokenQuery } from 'lib/queries/react-query/registry-server/getCsrfTokenQuery/getCsrfTokenQuery'; +import { GET_CSRF_QUERY_KEY } from 'lib/queries/react-query/registry-server/getCsrfTokenQuery/getCsrfTokenQuery.constants'; +import { getFromCacheOrFetch } from 'lib/queries/react-query/utils/getFromCacheOrFetch'; import { apiUri, indexerApiUri } from './lib/apiUri'; @@ -23,7 +28,8 @@ export const AuthApolloProvider = ({ apolloClientFactory, children, }: AuthApolloProviderProps): any => { - const query = useQuery(getCsrfTokenQuery({})); + const reactQueryClient = useQueryClient(); + useQuery(getCsrfTokenQuery({})); // https://www.apollographql.com/docs/react/networking/authentication const httpLink = createHttpLink({ @@ -36,12 +42,19 @@ export const AuthApolloProvider = ({ }); // https://www.apollographql.com/docs/react/api/link/apollo-link-context/#overview - const authLink = setContext((_, { headers }) => ({ - headers: { - ...headers, - 'X-CSRF-TOKEN': query.data, - }, - })); + const authLink = setContext(async (_, { headers }) => { + const csrfToken = await getFromCacheOrFetch({ + query: getCsrfTokenQuery({}), + reactQueryClient: reactQueryClient, + }); + + return { + headers: { + ...headers, + 'X-CSRF-TOKEN': csrfToken, + }, + }; + }); const defaultOptions: DefaultOptions = { watchQuery: { @@ -54,6 +67,43 @@ export const AuthApolloProvider = ({ }, }; + const errorLink = onError(({ operation, networkError, forward }) => { + if ( + networkError && + networkError.message.includes('status code 403') && + operation.operationName !== 'GetCurrentAccount' + ) { + const observable = new Observable>>( + observer => { + (async () => { + try { + await reactQueryClient.invalidateQueries({ + queryKey: [GET_CSRF_QUERY_KEY], + }); + // trigger a call to get a new CSRF token and refill the cache earlier + await getFromCacheOrFetch({ + query: getCsrfTokenQuery({}), + reactQueryClient: reactQueryClient, + }); + + const subscriber = { + next: observer.next.bind(observer), + error: observer.error.bind(observer), + complete: observer.complete.bind(observer), + }; + + forward(operation).subscribe(subscriber); + } catch (err) { + observer.error(err); + } + })(); + }, + ); + + return observable; + } + }); + const mainLink = authLink.concat(httpLink); const splitLink = split( @@ -63,7 +113,7 @@ export const AuthApolloProvider = ({ return operationName.startsWith('Indexer'); }, indexerHttpLink, - ApolloLink.from([mainLink]), + ApolloLink.from([errorLink, mainLink]), ); apolloClientFactory.prepare({ diff --git a/web-marketplace/src/lib/queries/react-query/registry-server/getCsrfTokenQuery/getCsrfTokenQuery.constants.ts b/web-marketplace/src/lib/queries/react-query/registry-server/getCsrfTokenQuery/getCsrfTokenQuery.constants.ts new file mode 100644 index 0000000000..84ed49f918 --- /dev/null +++ b/web-marketplace/src/lib/queries/react-query/registry-server/getCsrfTokenQuery/getCsrfTokenQuery.constants.ts @@ -0,0 +1 @@ +export const GET_CSRF_QUERY_KEY = 'csrfToken'; diff --git a/web-marketplace/src/lib/queries/react-query/registry-server/getCsrfTokenQuery/getCsrfTokenQuery.ts b/web-marketplace/src/lib/queries/react-query/registry-server/getCsrfTokenQuery/getCsrfTokenQuery.ts index 558ef70698..353162823a 100644 --- a/web-marketplace/src/lib/queries/react-query/registry-server/getCsrfTokenQuery/getCsrfTokenQuery.ts +++ b/web-marketplace/src/lib/queries/react-query/registry-server/getCsrfTokenQuery/getCsrfTokenQuery.ts @@ -1,5 +1,6 @@ import { apiUri } from 'lib/apiUri'; +import { GET_CSRF_QUERY_KEY } from './getCsrfTokenQuery.constants'; import { ReactQueryCsrfTokenProps, ReactQueryCsrfTokenResponse, @@ -8,7 +9,7 @@ import { export const getCsrfTokenQuery = ({ ...params }: ReactQueryCsrfTokenProps): ReactQueryCsrfTokenResponse => ({ - queryKey: ['csrfToken'], + queryKey: [GET_CSRF_QUERY_KEY], queryFn: async () => { const resp = await fetch(`${apiUri}/marketplace/v1/csrfToken`, { method: 'GET',