From f34736583390bde9e8fcd9f23989acd5255ffdad Mon Sep 17 00:00:00 2001 From: nsrCodes Date: Thu, 4 Apr 2024 07:51:07 +0530 Subject: [PATCH] feat: relay authorization header on redirect --- package-lock.json | 1 + package.json | 1 + .../helpers/proxy_ctx_helper.js | 78 ++++++++++++------- src/components/proxy-middleware/index.js | 6 +- .../processors/redirect_processor.js | 13 +++- 5 files changed, 69 insertions(+), 30 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1e68d0e..87f836f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "mime-types": "^2.1.35", "mkdirp": "^0.5.5", "node-forge": "^1.3.0", + "qs": "^6.12.0", "semaphore": "^1.1.0", "ua-parser-js": "^1.0.37", "url": "^0.11.3", diff --git a/package.json b/package.json index 93759af..10bdcbb 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "mime-types": "^2.1.35", "mkdirp": "^0.5.5", "node-forge": "^1.3.0", + "qs": "^6.12.0", "semaphore": "^1.1.0", "ua-parser-js": "^1.0.37", "url": "^0.11.3", diff --git a/src/components/proxy-middleware/helpers/proxy_ctx_helper.js b/src/components/proxy-middleware/helpers/proxy_ctx_helper.js index 75ca5ab..5d6ae98 100644 --- a/src/components/proxy-middleware/helpers/proxy_ctx_helper.js +++ b/src/components/proxy-middleware/helpers/proxy_ctx_helper.js @@ -9,6 +9,7 @@ import { CONSTANTS as GLOBAL_CONSTANTS, } from "@requestly/requestly-core" +import qs from 'qs'; function extractUrlComponent(url, name) { // need this in proxy const myUrl = new URL(url); @@ -37,38 +38,14 @@ function extractUrlComponent(url, name) { // need this in proxy * @returns object { paramName -> [value1, value2] } */ export function getQueryParamsMap(queryString) { - var map = {}, - queryParams; - - if (!queryString || queryString === "?") { - return map; - } - - if (queryString[0] === "?") { - queryString = queryString.substr(1); - } - - queryParams = queryString.split("&"); - - queryParams.forEach(function (queryParam) { - var paramName = queryParam.split("=")[0], - paramValue = queryParam.split("=")[1]; - - // We are keeping value of param as array so that in future we can support multiple param values of same name - // And we do not want to lose the params if url already contains multiple params of same name - map[paramName] = map[paramName] || []; - map[paramName].push(paramValue); - }); - - return map; + return qs.parse(queryString, { ignoreQueryPrefix: true }); } - export const get_request_url = (ctx) => { return ( (ctx.isSSL ? "https://" : "http://") + - ctx.clientToProxyRequest.headers.host + - ctx.clientToProxyRequest.url + ctx.proxyToServerRequestOptions.headers.host || ctx.proxyToServerRequestOptions.host + + ctx.proxyToServerRequestOptions.url ); }; @@ -150,3 +127,50 @@ export const getResponseStatusCode = (ctx) => { return ctx.serverToProxyResponse.statusCode; } }; + + +const HEADERS_IGNORED_ON_REDIRECT = ["authorization"]; +const CUSTOM_QUERY_PARAM_PREFIX = "x-rq-"; + +export function getExtraQueryParamsForRedirect(ctx) { + const customQueryParams = {}; + HEADERS_IGNORED_ON_REDIRECT.forEach((ignoredHeader) => { + if(ctx.rq.original_request.headers[ignoredHeader]) { + const customQueryParamKey = `${CUSTOM_QUERY_PARAM_PREFIX}${ignoredHeader}`; + customQueryParams[customQueryParamKey] = ctx.rq.original_request.headers[ignoredHeader]; + } + }) + return customQueryParams; +} + +function extractDataFromRQQueryParams(ctx) { + const metaData = {}; + const queryParams = get_json_query_params(ctx); + for (const param in queryParams) { + if (param.startsWith(CUSTOM_QUERY_PARAM_PREFIX)) { + const key = param.substring(CUSTOM_QUERY_PARAM_PREFIX.length); + metaData[key] = queryParams[param] + delete queryParams[param]; + } + } + + const newUrl = new URL(get_request_url(ctx)); + for (const [key, value] of Object.entries(queryParams)) { + newUrl.searchParams.set(key, value); + } + + ctx.proxyToServerRequestOptions.path = newUrl.pathname; + ctx.proxyToServerRequestOptions.url = newUrl.toString(); + + ctx.rq.set_original_request({ path: newUrl.pathname, query_params: queryParams }); +} + +export function handleRQMetadataInQueryParam(ctx) { + const headersToBeAddedToRequest = extractDataFromRQQueryParams(ctx); + if (headersToBeAddedToRequest) { + for (const [name, value] in Object.entries(headersToBeAddedToRequest)) { + ctx.proxyToServerRequestOptions.headers[name] = value; + } + } + ctx.rq.set_original_request({ headers: ctx.proxyToServerRequestOptions.headers }); +} \ No newline at end of file diff --git a/src/components/proxy-middleware/index.js b/src/components/proxy-middleware/index.js index 580f065..5a28e27 100644 --- a/src/components/proxy-middleware/index.js +++ b/src/components/proxy-middleware/index.js @@ -9,8 +9,8 @@ import { getResponseHeaders, getResponseStatusCode, get_request_options, - get_request_url, get_response_options, + handleRQMetadataInQueryParam, } from "./helpers/proxy_ctx_helper"; import RulesMiddleware from "./middlewares/rules_middleware"; @@ -124,6 +124,9 @@ class ProxyMiddlewareManager { ctx.rq = new CtxRQNamespace(); ctx.rq.set_original_request(get_request_options(ctx)); ctx.proxyToServerRequestOptions.rejectUnauthorized = false; + + handleRQMetadataInQueryParam(ctx); + // Figure out a way to enable/disable middleware dynamically // instead of re-initing this again const rules_middleware = new RulesMiddleware( @@ -179,7 +182,6 @@ class ProxyMiddlewareManager { const contentType = getContentType(contentTypeHeader); const parsedBody = bodyParser(contentTypeHeader, body); - // Request body before any modifications let pre_final_body = parsedBody || body.toString("utf8"); ctx.rq.set_original_request({ body: pre_final_body }); ctx.rq_request_body = pre_final_body; diff --git a/src/components/proxy-middleware/rule_action_processor/processors/redirect_processor.js b/src/components/proxy-middleware/rule_action_processor/processors/redirect_processor.js index adf9d31..b97c0c2 100644 --- a/src/components/proxy-middleware/rule_action_processor/processors/redirect_processor.js +++ b/src/components/proxy-middleware/rule_action_processor/processors/redirect_processor.js @@ -1,5 +1,6 @@ import { PROXY_HANDLER_TYPE } from "../../../../lib/proxy"; import { + getExtraQueryParamsForRedirect, get_request_url, is_request_preflight, } from "../../helpers/proxy_ctx_helper"; @@ -28,7 +29,17 @@ const process_redirect_action = async (action, ctx) => { } const current_url = get_request_url(ctx); - const new_url = action.url; + let new_url = action.url; + + // Sending back authorization header in query param for new location + const extraQueryParams = getExtraQueryParamsForRedirect(ctx); + if (extraQueryParams) { + const url = new URL(new_url); + for (const [key, value] of Object.entries(extraQueryParams)) { + url.searchParams.set(key, value); + } + new_url = url.toString(); + } const request_url = current_url.replace(/www\./g, "");