auth0
Version:
Auth0 Node.js SDK for the Management API v2.
313 lines (312 loc) • 12 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { toJson } from "../json.mjs";
import { createLogger } from "../logging/logger.mjs";
import { createRequestUrl } from "./createRequestUrl.mjs";
import { EndpointSupplier } from "./EndpointSupplier.mjs";
import { getErrorResponseBody } from "./getErrorResponseBody.mjs";
import { getFetchFn } from "./getFetchFn.mjs";
import { getRequestBody } from "./getRequestBody.mjs";
import { getResponseBody } from "./getResponseBody.mjs";
import { Headers } from "./Headers.mjs";
import { makeRequest } from "./makeRequest.mjs";
import { abortRawResponse, toRawResponse, unknownRawResponse } from "./RawResponse.mjs";
import { requestWithRetries } from "./requestWithRetries.mjs";
const SENSITIVE_HEADERS = new Set([
"authorization",
"www-authenticate",
"x-api-key",
"api-key",
"apikey",
"x-api-token",
"x-auth-token",
"auth-token",
"cookie",
"set-cookie",
"proxy-authorization",
"proxy-authenticate",
"x-csrf-token",
"x-xsrf-token",
"x-session-token",
"x-access-token",
]);
function redactHeaders(headers) {
const filtered = {};
for (const [key, value] of headers instanceof Headers ? headers.entries() : Object.entries(headers)) {
if (SENSITIVE_HEADERS.has(key.toLowerCase())) {
filtered[key] = "[REDACTED]";
}
else {
filtered[key] = value;
}
}
return filtered;
}
const SENSITIVE_QUERY_PARAMS = new Set([
"api_key",
"api-key",
"apikey",
"token",
"access_token",
"access-token",
"auth_token",
"auth-token",
"password",
"passwd",
"secret",
"api_secret",
"api-secret",
"apisecret",
"key",
"session",
"session_id",
"session-id",
]);
function redactQueryParameters(queryParameters) {
if (queryParameters == null) {
return queryParameters;
}
const redacted = {};
for (const [key, value] of Object.entries(queryParameters)) {
if (SENSITIVE_QUERY_PARAMS.has(key.toLowerCase())) {
redacted[key] = "[REDACTED]";
}
else {
redacted[key] = value;
}
}
return redacted;
}
function redactUrl(url) {
const protocolIndex = url.indexOf("://");
if (protocolIndex === -1)
return url;
const afterProtocol = protocolIndex + 3;
// Find the first delimiter that marks the end of the authority section
const pathStart = url.indexOf("/", afterProtocol);
let queryStart = url.indexOf("?", afterProtocol);
let fragmentStart = url.indexOf("#", afterProtocol);
const firstDelimiter = Math.min(pathStart === -1 ? url.length : pathStart, queryStart === -1 ? url.length : queryStart, fragmentStart === -1 ? url.length : fragmentStart);
// Find the LAST @ before the delimiter (handles multiple @ in credentials)
let atIndex = -1;
for (let i = afterProtocol; i < firstDelimiter; i++) {
if (url[i] === "@") {
atIndex = i;
}
}
if (atIndex !== -1) {
url = `${url.slice(0, afterProtocol)}[REDACTED]@${url.slice(atIndex + 1)}`;
}
// Recalculate queryStart since url might have changed
queryStart = url.indexOf("?");
if (queryStart === -1)
return url;
fragmentStart = url.indexOf("#", queryStart);
const queryEnd = fragmentStart !== -1 ? fragmentStart : url.length;
const queryString = url.slice(queryStart + 1, queryEnd);
if (queryString.length === 0)
return url;
// FAST PATH: Quick check if any sensitive keywords present
// Using indexOf is faster than regex for simple substring matching
const lower = queryString.toLowerCase();
const hasSensitive = lower.includes("token") ||
lower.includes("key") ||
lower.includes("password") ||
lower.includes("passwd") ||
lower.includes("secret") ||
lower.includes("session") ||
lower.includes("auth");
if (!hasSensitive) {
return url;
}
// SLOW PATH: Parse and redact
const redactedParams = [];
const params = queryString.split("&");
for (const param of params) {
const equalIndex = param.indexOf("=");
if (equalIndex === -1) {
redactedParams.push(param);
continue;
}
const key = param.slice(0, equalIndex);
let shouldRedact = SENSITIVE_QUERY_PARAMS.has(key.toLowerCase());
if (!shouldRedact && key.includes("%")) {
try {
const decodedKey = decodeURIComponent(key);
shouldRedact = SENSITIVE_QUERY_PARAMS.has(decodedKey.toLowerCase());
}
catch (_a) { }
}
redactedParams.push(shouldRedact ? `${key}=[REDACTED]` : param);
}
return url.slice(0, queryStart + 1) + redactedParams.join("&") + url.slice(queryEnd);
}
function getHeaders(args) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
const newHeaders = new Headers();
newHeaders.set("Accept", args.responseType === "json" ? "application/json" : args.responseType === "text" ? "text/plain" : "*/*");
if (args.body !== undefined && args.contentType != null) {
newHeaders.set("Content-Type", args.contentType);
}
if (args.headers == null) {
return newHeaders;
}
for (const [key, value] of Object.entries(args.headers)) {
const result = yield EndpointSupplier.get(value, { endpointMetadata: (_a = args.endpointMetadata) !== null && _a !== void 0 ? _a : {} });
if (typeof result === "string") {
newHeaders.set(key, result);
continue;
}
if (result == null) {
continue;
}
newHeaders.set(key, `${result}`);
}
return newHeaders;
});
}
export function fetcherImpl(args) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c;
const url = createRequestUrl(args.url, args.queryParameters);
const requestBody = yield getRequestBody({
body: args.body,
type: (_a = args.requestType) !== null && _a !== void 0 ? _a : "other",
});
const fetchFn = (_b = args.fetchFn) !== null && _b !== void 0 ? _b : (yield getFetchFn());
const headers = yield getHeaders(args);
const logger = createLogger(args.logging);
if (logger.isDebug()) {
const metadata = {
method: args.method,
url: redactUrl(url),
headers: redactHeaders(headers),
queryParameters: redactQueryParameters(args.queryParameters),
hasBody: requestBody != null,
};
logger.debug("Making HTTP request", metadata);
}
try {
const response = yield requestWithRetries(() => __awaiter(this, void 0, void 0, function* () {
return makeRequest(fetchFn, url, args.method, headers, requestBody, args.timeoutMs, args.abortSignal, args.withCredentials, args.duplex);
}), args.maxRetries);
if (response.status >= 200 && response.status < 400) {
if (logger.isDebug()) {
const metadata = {
method: args.method,
url: redactUrl(url),
statusCode: response.status,
responseHeaders: redactHeaders(response.headers),
};
logger.debug("HTTP request succeeded", metadata);
}
const body = yield getResponseBody(response, args.responseType);
return {
ok: true,
body: body,
headers: response.headers,
rawResponse: toRawResponse(response),
};
}
else {
if (logger.isError()) {
const metadata = {
method: args.method,
url: redactUrl(url),
statusCode: response.status,
responseHeaders: redactHeaders(Object.fromEntries(response.headers.entries())),
};
logger.error("HTTP request failed with error status", metadata);
}
return {
ok: false,
error: {
reason: "status-code",
statusCode: response.status,
body: yield getErrorResponseBody(response),
},
rawResponse: toRawResponse(response),
};
}
}
catch (error) {
if ((_c = args.abortSignal) === null || _c === void 0 ? void 0 : _c.aborted) {
if (logger.isError()) {
const metadata = {
method: args.method,
url: redactUrl(url),
};
logger.error("HTTP request was aborted", metadata);
}
return {
ok: false,
error: {
reason: "unknown",
errorMessage: "The user aborted a request",
},
rawResponse: abortRawResponse,
};
}
else if (error instanceof Error && error.name === "AbortError") {
if (logger.isError()) {
const metadata = {
method: args.method,
url: redactUrl(url),
timeoutMs: args.timeoutMs,
};
logger.error("HTTP request timed out", metadata);
}
return {
ok: false,
error: {
reason: "timeout",
},
rawResponse: abortRawResponse,
};
}
else if (error instanceof Error) {
if (logger.isError()) {
const metadata = {
method: args.method,
url: redactUrl(url),
errorMessage: error.message,
};
logger.error("HTTP request failed with error", metadata);
}
return {
ok: false,
error: {
reason: "unknown",
errorMessage: error.message,
},
rawResponse: unknownRawResponse,
};
}
if (logger.isError()) {
const metadata = {
method: args.method,
url: redactUrl(url),
error: toJson(error),
};
logger.error("HTTP request failed with unknown error", metadata);
}
return {
ok: false,
error: {
reason: "unknown",
errorMessage: toJson(error),
},
rawResponse: unknownRawResponse,
};
}
});
}
export const fetcher = fetcherImpl;