Commit 65fd3abf authored by tom goriunov's avatar tom goriunov Committed by GitHub

Merge pull request #508 from blockscout/cors-refactoring

call api from client on preview stands
parents ffc74fa6 a8343da3
......@@ -2,6 +2,7 @@ import { compile } from 'path-to-regexp';
import appConfig from 'configs/app/config';
import isNeedProxy from './isNeedProxy';
import { RESOURCES } from './resources';
import type { ApiResource, ResourceName } from './resources';
......@@ -10,23 +11,10 @@ export default function buildUrl(
pathParams?: Record<string, string | undefined>,
queryParams?: Record<string, string | Array<string> | number | undefined>,
) {
// FIXME
// 1. I was not able to figure out how to send CORS with credentials from localhost
// unsuccessfully tried different ways, even custom local dev domain
// so for local development we have to use next.js api as proxy server
// 2. and there is an issue with API and csrf token
// for some reason API will reply with error "Bad request" to any PUT / POST CORS request
// even though valid csrf-token is passed in header
// we also can pass token in request body but in this case API will replay with "Forbidden" error
// @nikitosing said it will take a lot of time to debug this problem on back-end side, maybe he'll change his mind in future :)
// To sum up, we are using next.js proxy for all instances where app host is not the same as API host (incl. localhost)
// will need to change the condition if there are more micro services that need authentication and DB state changes
const needProxy = appConfig.host !== appConfig.api.host;
const resource: ApiResource = typeof _resource === 'string' ? RESOURCES[_resource] : _resource;
const baseUrl = needProxy ? appConfig.baseUrl : (resource.endpoint || appConfig.api.endpoint);
const baseUrl = isNeedProxy() ? appConfig.baseUrl : (resource.endpoint || appConfig.api.endpoint);
const basePath = resource.basePath !== undefined ? resource.basePath : appConfig.api.basePath;
const path = needProxy ? '/node-api/proxy' + basePath + resource.path : basePath + resource.path;
const path = isNeedProxy() ? '/node-api/proxy' + basePath + resource.path : basePath + resource.path;
const url = new URL(compile(path)(pathParams), baseUrl);
queryParams && Object.entries(queryParams).forEach(([ key, value ]) => {
......
import appConfig from 'configs/app/config';
// FIXME
// I was not able to figure out how to send CORS with credentials from localhost
// unsuccessfully tried different ways, even custom local dev domain
// so for local development we have to use next.js api as proxy server
export default function isNeedProxy() {
return appConfig.host === 'localhost' && appConfig.host !== appConfig.api.host;
}
......@@ -24,8 +24,9 @@ export default function fetchFactory(
});
return nodeFetch(url, {
headers,
...init,
headers,
body: init?.body ? JSON.stringify(init.body) : undefined,
});
};
}
......@@ -28,7 +28,6 @@ export default function useFetch() {
headers: {
...(hasBody ? { 'Content-type': 'application/json' } : undefined),
...params?.headers,
// ...(token ? { 'x-csrf-token': token } : {}),
},
};
......
import { useQuery } from '@tanstack/react-query';
import buildUrl from 'lib/api/buildUrl';
import isNeedProxy from 'lib/api/isNeedProxy';
import { getResourceKey } from 'lib/api/useApiQuery';
import * as cookies from 'lib/cookies';
import useFetch from 'lib/hooks/useFetch';
export default function useGetCsrfToken() {
const nodeApiFetch = useFetch();
useQuery(getResourceKey('csrf'), async() => {
if (!isNeedProxy()) {
const url = buildUrl('csrf');
const apiResponse = await fetch(url, { credentials: 'include' });
const csrfFromHeader = apiResponse.headers.get('x-bs-account-csrf');
// eslint-disable-next-line no-console
console.log('>>> RESPONSE HEADERS <<<');
// eslint-disable-next-line no-console
console.table([ {
'content-length': apiResponse.headers.get('content-length'),
'x-bs-account-csrf': csrfFromHeader,
} ]);
return csrfFromHeader ? { token: csrfFromHeader } : undefined;
}
return nodeApiFetch('/node-api/csrf');
}, {
enabled: Boolean(cookies.get(cookies.NAMES.API_TOKEN)),
});
}
import { Flex } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import React from 'react';
import appConfig from 'configs/app/config';
import buildUrl from 'lib/api/buildUrl';
import { getResourceKey } from 'lib/api/useApiQuery';
import * as cookies from 'lib/cookies';
import useFetch from 'lib/hooks/useFetch';
import useGetCsrfToken from 'lib/hooks/useGetCsrfToken';
import AppError from 'ui/shared/AppError/AppError';
import ErrorBoundary from 'ui/shared/ErrorBoundary';
import ErrorInvalidTxHash from 'ui/shared/ErrorInvalidTxHash';
......@@ -27,28 +22,8 @@ const Page = ({
isHomePage,
renderHeader,
}: Props) => {
const nodeApiFetch = useFetch();
useQuery(getResourceKey('csrf'), async() => {
if (appConfig.host === appConfig.api.host) {
const url = buildUrl('csrf');
const apiResponse = await fetch(url, { credentials: 'include' });
const csrfFromHeader = apiResponse.headers.get('x-bs-account-csrf');
// eslint-disable-next-line no-console
console.log('>>> RESPONSE HEADERS <<<');
// eslint-disable-next-line no-console
console.table([ {
'content-length': apiResponse.headers.get('content-length'),
'x-bs-account-csrf': csrfFromHeader,
} ]);
return csrfFromHeader ? { token: csrfFromHeader } : undefined;
}
return nodeApiFetch('/node-api/csrf');
}, {
enabled: Boolean(cookies.get(cookies.NAMES.API_TOKEN)),
});
useGetCsrfToken();
const renderErrorScreen = React.useCallback((error?: Error) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment