UNPKG

@sap-cloud-sdk/http-client

Version:

SAP Cloud SDK for JavaScript http-client

135 lines 5.22 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.csrf = csrf; exports.buildCsrfFetchHeaders = buildCsrfFetchHeaders; const util_1 = require("@sap-cloud-sdk/util"); const axios_1 = __importDefault(require("axios")); const internal_1 = require("@sap-cloud-sdk/resilience/internal"); const logger = (0, util_1.createLogger)('csrf-middleware'); /** * Middleware for fetching a CSRF token. This middleware is added to all request per default. * Use the `fetchCsrfToken` option to disable it. * @param options - Options like URL or method to configure the token fetching. * @returns The middleware for fetching CSRF tokens. */ function csrf(options) { return (middlewareOptions) => async (requestConfig) => { if (!needsCsrfToken(requestConfig)) { return middlewareOptions.fn(requestConfig); } const csrfToken = await makeCsrfRequests(requestConfig, { ...options, ...middlewareOptions }); if (csrfToken?.cookie) { csrfToken.cookie = requestConfig.headers?.cookie ? [requestConfig.headers?.cookie, csrfToken?.cookie].join(';') : csrfToken?.cookie; } requestConfig.headers = { ...requestConfig.headers, ...csrfToken }; return middlewareOptions.fn(requestConfig); }; } function needsCsrfToken(requestConfig) { if (requestConfig.method.toLowerCase() === 'get') { logger.debug('Method is GET no CSRF token needed.'); return false; } if ((0, util_1.pickValueIgnoreCase)(requestConfig.headers, 'x-csrf-token')) { logger.debug('CSRF token header was already provided. Existing token used.'); return false; } return true; } function appendSlash(requestConfig) { if (!requestConfig.url) { requestConfig.url = '/'; } else if (!requestConfig.url.endsWith('/')) { requestConfig.url = `${requestConfig.url}/`; } return requestConfig; } function removeSlash(requestConfig) { if (requestConfig.url.endsWith('/')) { requestConfig.url = (0, util_1.removeTrailingSlashes)(requestConfig.url); } return requestConfig; } function getCsrfToken(headers) { return Object.values((0, util_1.pickIgnoreCase)(headers, 'x-csrf-token'))[0]; } function getSetCookieHeader(headers) { const cookies = Object.values((0, util_1.pickIgnoreCase)(headers, 'set-cookie')); // On cookie format: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie return (0, util_1.flatten)(cookies) .map((cookie) => cookie.split(';')[0]) .join(';'); } /** * @param headers - Request header information. * @returns CSRF related headers. * @internal */ function buildCsrfFetchHeaders(headers) { const contentLengthHeaderKey = (0, util_1.first)(Object.keys((0, util_1.pickIgnoreCase)(headers, 'content-length'))) || 'content-length'; return { 'x-csrf-token': 'Fetch', ...headers, [contentLengthHeaderKey]: 0 }; } async function makeCsrfRequest(requestConfig, options) { try { const response = await (0, internal_1.executeWithMiddleware)(options.middleware, { fn: axios_1.default.request, fnArgument: requestConfig, context: options.context }); return findCsrfHeader(response.headers); } catch (error) { if (findCsrfHeader(error.response?.headers)) { return findCsrfHeader(error.response?.headers); } logger.warn(new util_1.ErrorWithCause(`Failed to get CSRF token from URL: ${requestConfig.url}.`, error)); } } function findCsrfHeader(headers) { if (!headers) { return; } const csrfHeader = getCsrfToken(headers); if (!csrfHeader) { return; } const cookieHeader = getSetCookieHeader(headers) ? { cookie: getSetCookieHeader(headers) } : {}; return { 'x-csrf-token': csrfHeader, ...cookieHeader }; } async function makeCsrfRequests(requestConfig, options) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { data, params, parameterEncoder, ...requestConfigWithoutData } = requestConfig; const axiosConfig = { ...requestConfigWithoutData, method: options.method || 'head', url: options.url || requestConfig.url, headers: buildCsrfFetchHeaders(requestConfig.headers) }; // If the user set the URL to fetch the token we only do if (options.url) { return makeCsrfRequest(axiosConfig, options); } // The S/4 does a redirect if the CSRF token is fetched in case the '/' is not in the URL. // TODO: remove once https://github.com/axios/axios/issues/3369 is really fixed. Issue is closed but problem stays. // We try first with slash and then without /* eslint-disable no-return-await */ return ((await makeCsrfRequest(appendSlash(axiosConfig), options)) ?? (await makeCsrfRequest(removeSlash(axiosConfig), options))); } //# sourceMappingURL=csrf-token-middleware.js.map