feat: Extract authentication when using the cURL importer (#423)

This commit is contained in:
Jeroen van der Merwe
2026-05-21 18:00:22 +02:00
committed by GitHub
parent 1307ea4e67
commit 463cc6f5a3
2 changed files with 229 additions and 6 deletions
+93 -6
View File
@@ -181,6 +181,78 @@ export function convertCurl(rawData: string) {
};
}
interface ExtractedAuthentication {
authenticationType: string | null;
authentication: Record<string, string>;
filteredHeaders: HttpUrlParameter[]; // headers without authorization
}
function extractAuthenticationFromHeaders(headers: HttpUrlParameter[]): ExtractedAuthentication {
const authorizationHeaderIndex = headers.findIndex(
(h) => h.name.toLowerCase() === "authorization",
);
const authorizationHeader = headers[authorizationHeaderIndex];
if (authorizationHeader == null) {
return {
authenticationType: null,
authentication: {},
filteredHeaders: headers,
};
}
const value = authorizationHeader.value.trim();
const spaceIndex = value.indexOf(" ");
if (spaceIndex <= 0) {
return {
authenticationType: null,
authentication: {},
filteredHeaders: headers,
};
}
const scheme = value.slice(0, spaceIndex).toLowerCase();
const credentials = value.slice(spaceIndex + 1).trim();
// Bearer authentication (RFC 6750)
if (scheme === "bearer") {
const filteredHeaders = headers.filter((_, i) => i !== authorizationHeaderIndex);
return {
authenticationType: "bearer",
authentication: { token: credentials, prefix: "Bearer" },
filteredHeaders,
};
}
// Basic authentication (RFC 7617)
if (scheme === "basic") {
try {
const decoded = Buffer.from(credentials, "base64").toString();
const colonIndex = decoded.indexOf(":");
if (colonIndex > 0) {
const filteredHeaders = headers.filter((_, i) => i !== authorizationHeaderIndex);
return {
authenticationType: "basic",
authentication: {
username: decoded.slice(0, colonIndex),
password: decoded.slice(colonIndex + 1),
},
filteredHeaders,
};
}
} catch {
// Invalid base64, keep header as-is
}
}
return {
authenticationType: null,
authentication: {},
filteredHeaders: headers,
};
}
function importCommand(parseEntries: string[], workspaceId: string) {
// ~~~~~~~~~~~~~~~~~~~~~ //
// Collect all the flags //
@@ -323,8 +395,23 @@ function importCommand(parseEntries: string[], workspaceId: string) {
});
}
// Extract authentication from Authorization headers (Bearer/Basic)
const {
authenticationType: extractedAuthenticationType,
authentication: extractedAuthentication,
filteredHeaders,
} = extractAuthenticationFromHeaders(headers);
// Use extracted authentication from header if found, otherwise fall back to -u/--user parsing
const finalAuthenticationType = extractedAuthenticationType || authenticationType;
const finalAuthentication = extractedAuthenticationType
? extractedAuthentication
: authentication;
// Body (Text or Blob)
const contentTypeHeader = headers.find((header) => header.name.toLowerCase() === "content-type");
const contentTypeHeader = filteredHeaders.find(
(header) => header.name.toLowerCase() === "content-type",
);
const mimeType = contentTypeHeader ? contentTypeHeader.value.split(";")[0]?.trim() : null;
// Extract boundary from Content-Type header for multipart parsing
@@ -398,7 +485,7 @@ function importCommand(parseEntries: string[], workspaceId: string) {
value: decodeURIComponent(parameter.value || ""),
})),
};
headers.push({
filteredHeaders.push({
name: "Content-Type",
value: "application/x-www-form-urlencoded",
enabled: true,
@@ -419,7 +506,7 @@ function importCommand(parseEntries: string[], workspaceId: string) {
form: formDataParams,
};
if (mimeType == null) {
headers.push({
filteredHeaders.push({
name: "Content-Type",
value: "multipart/form-data",
enabled: true,
@@ -442,9 +529,9 @@ function importCommand(parseEntries: string[], workspaceId: string) {
urlParameters,
url,
method,
headers,
authentication,
authenticationType,
headers: filteredHeaders,
authentication: finalAuthentication,
authenticationType: finalAuthenticationType,
body,
bodyType,
folderId: null,