feat(common): authentication strategy improvements (#5130)

Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
Anwarul Islam 2025-06-25 22:42:09 +06:00 committed by GitHub
parent 78a165dcbb
commit 965e4e1d44
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 569 additions and 373 deletions

View file

@ -0,0 +1,92 @@
import {
Environment,
HoppRESTAuth,
HoppRESTHeader,
HoppRESTParam,
HoppRESTRequest,
} from "@hoppscotch/data"
import {
generateApiKeyAuthHeaders,
generateApiKeyAuthParams,
} from "./types/api-key"
import {
generateAwsSignatureAuthHeaders,
generateAwsSignatureAuthParams,
} from "./types/aws-signature"
import { generateBasicAuthHeaders } from "./types/basic"
import { generateBearerAuthHeaders } from "./types/bearer"
import { generateDigestAuthHeaders } from "./types/digest"
import { generateHawkAuthHeaders } from "./types/hawk"
import { generateJwtAuthHeaders, generateJwtAuthParams } from "./types/jwt"
import {
generateOAuth2AuthHeaders,
generateOAuth2AuthParams,
} from "./types/oauth2"
/**
* Generate headers for the given auth type using function-based approach
*/
export async function generateAuthHeaders(
auth: HoppRESTAuth,
request: HoppRESTRequest,
envVars: Environment["variables"],
showKeyIfSecret = false
): Promise<HoppRESTHeader[]> {
switch (auth.authType) {
case "basic":
return generateBasicAuthHeaders(auth, request, envVars, showKeyIfSecret)
case "bearer":
return generateBearerAuthHeaders(auth, request, envVars, showKeyIfSecret)
case "api-key":
return auth.addTo === "HEADERS"
? generateApiKeyAuthHeaders(auth, request, envVars, showKeyIfSecret)
: []
case "oauth-2":
return generateOAuth2AuthHeaders(auth, request, envVars, showKeyIfSecret)
case "digest":
return generateDigestAuthHeaders(auth, request, envVars, showKeyIfSecret)
case "aws-signature":
return generateAwsSignatureAuthHeaders(
auth,
request,
envVars,
showKeyIfSecret
)
case "hawk":
return generateHawkAuthHeaders(auth, request, envVars, showKeyIfSecret)
case "jwt":
return generateJwtAuthHeaders(auth, request, envVars, showKeyIfSecret)
default:
return []
}
}
/**
* Generate query parameters for the given auth type using function-based approach
*/
export async function generateAuthParams(
auth: HoppRESTAuth,
request: HoppRESTRequest,
envVars: Environment["variables"],
showKeyIfSecret = false
): Promise<HoppRESTParam[]> {
switch (auth.authType) {
case "api-key":
return auth.addTo === "QUERY_PARAMS"
? generateApiKeyAuthParams(auth, request, envVars, showKeyIfSecret)
: []
case "oauth-2":
return generateOAuth2AuthParams(auth, request, envVars, showKeyIfSecret)
case "aws-signature":
return generateAwsSignatureAuthParams(
auth,
request,
envVars,
showKeyIfSecret
)
case "jwt":
return generateJwtAuthParams(auth, request, envVars, showKeyIfSecret)
default:
return []
}
}

View file

@ -19,7 +19,8 @@ export const replaceTemplateStringsInObjectValues = <
? restTabsService.currentActiveTab.value.document.request.requestVariables.map( ? restTabsService.currentActiveTab.value.document.request.requestVariables.map(
({ key, value }) => ({ ({ key, value }) => ({
key, key,
value, initialValue: value,
currentValue: value,
secret: false, secret: false,
}) })
) )

View file

@ -0,0 +1,49 @@
import {
parseTemplateString,
HoppRESTAuth,
HoppRESTRequest,
Environment,
HoppRESTHeader,
HoppRESTParam,
} from "@hoppscotch/data"
export async function generateApiKeyAuthHeaders(
auth: HoppRESTAuth & { authType: "api-key" },
request: HoppRESTRequest,
envVars: Environment["variables"],
showKeyIfSecret = false
): Promise<HoppRESTHeader[]> {
if (auth.addTo !== "HEADERS") return []
return [
{
active: true,
key: parseTemplateString(auth.key, envVars, false, showKeyIfSecret),
value: parseTemplateString(
auth.value ?? "",
envVars,
false,
showKeyIfSecret
),
description: "",
},
]
}
export async function generateApiKeyAuthParams(
auth: HoppRESTAuth & { authType: "api-key" },
request: HoppRESTRequest,
envVars: Environment["variables"],
showKeyIfSecret = false
): Promise<HoppRESTParam[]> {
if (auth.addTo !== "QUERY_PARAMS") return []
return [
{
active: true,
key: parseTemplateString(auth.key, envVars, false, showKeyIfSecret),
value: parseTemplateString(auth.value, envVars, false, showKeyIfSecret),
description: "",
},
]
}

View file

@ -0,0 +1,91 @@
import {
parseTemplateString,
HoppRESTAuth,
HoppRESTRequest,
Environment,
HoppRESTHeader,
HoppRESTParam,
} from "@hoppscotch/data"
import { AwsV4Signer } from "aws4fetch"
import { getFinalBodyFromRequest } from "~/helpers/utils/EffectiveURL"
export async function generateAwsSignatureAuthHeaders(
auth: HoppRESTAuth & { authType: "aws-signature" },
request: HoppRESTRequest,
envVars: Environment["variables"],
showKeyIfSecret = false
): Promise<HoppRESTHeader[]> {
if (auth.addTo !== "HEADERS") return []
const currentDate = new Date()
const amzDate = currentDate.toISOString().replace(/[:-]|\.\d{3}/g, "")
const { method, endpoint } = request
const body = getFinalBodyFromRequest(request, envVars)
const signer = new AwsV4Signer({
method: method,
body: body?.toString(),
datetime: amzDate,
accessKeyId: parseTemplateString(auth.accessKey, envVars),
secretAccessKey: parseTemplateString(auth.secretKey, envVars),
region: parseTemplateString(auth.region, envVars) ?? "us-east-1",
service: parseTemplateString(auth.serviceName, envVars),
sessionToken:
auth.serviceToken && parseTemplateString(auth.serviceToken, envVars),
url: parseTemplateString(endpoint, envVars),
})
const sign = await signer.sign()
const headers: HoppRESTHeader[] = []
sign.headers.forEach((value, key) => {
headers.push({
active: true,
key: key,
value: value,
description: "",
})
})
return headers
}
export async function generateAwsSignatureAuthParams(
auth: HoppRESTAuth & { authType: "aws-signature" },
request: HoppRESTRequest,
envVars: Environment["variables"],
showKeyIfSecret = false
): Promise<HoppRESTParam[]> {
if (auth.addTo !== "QUERY_PARAMS") return []
const currentDate = new Date()
const amzDate = currentDate.toISOString().replace(/[:-]|\.\d{3}/g, "")
const signer = new AwsV4Signer({
method: request.method,
datetime: amzDate,
signQuery: true,
accessKeyId: parseTemplateString(auth.accessKey, envVars),
secretAccessKey: parseTemplateString(auth.secretKey, envVars),
region: parseTemplateString(auth.region, envVars) ?? "us-east-1",
service: parseTemplateString(auth.serviceName, envVars),
sessionToken:
auth.serviceToken && parseTemplateString(auth.serviceToken, envVars),
url: parseTemplateString(request.endpoint, envVars),
})
const sign = await signer.sign()
const params: HoppRESTParam[] = []
for (const [key, value] of sign.url.searchParams) {
params.push({
active: true,
key: key,
value: value,
description: "",
})
}
return params
}

View file

@ -0,0 +1,37 @@
import {
parseTemplateString,
HoppRESTAuth,
HoppRESTRequest,
Environment,
HoppRESTHeader,
HoppRESTParam,
} from "@hoppscotch/data"
export async function generateBasicAuthHeaders(
auth: HoppRESTAuth & { authType: "basic" },
request: HoppRESTRequest,
envVars: Environment["variables"],
showKeyIfSecret = false
): Promise<HoppRESTHeader[]> {
const username = parseTemplateString(
auth.username,
envVars,
false,
showKeyIfSecret
)
const password = parseTemplateString(
auth.password,
envVars,
false,
showKeyIfSecret
)
return [
{
active: true,
key: "Authorization",
value: `Basic ${btoa(`${username}:${password}`)}`,
description: "",
},
]
}

View file

@ -0,0 +1,26 @@
import {
parseTemplateString,
HoppRESTAuth,
HoppRESTRequest,
Environment,
HoppRESTHeader,
HoppRESTParam,
} from "@hoppscotch/data"
export async function generateBearerAuthHeaders(
auth: HoppRESTAuth & { authType: "bearer" },
request: HoppRESTRequest,
envVars: Environment["variables"],
showKeyIfSecret = false
): Promise<HoppRESTHeader[]> {
const token = parseTemplateString(auth.token, envVars, false, showKeyIfSecret)
return [
{
active: true,
key: "Authorization",
value: `Bearer ${token}`,
description: "",
},
]
}

View file

@ -0,0 +1,66 @@
import {
parseTemplateString,
HoppRESTAuth,
HoppRESTRequest,
Environment,
HoppRESTHeader,
HoppRESTParam,
} from "@hoppscotch/data"
import {
DigestAuthParams,
fetchInitialDigestAuthInfo,
generateDigestAuthHeader,
} from "../digest"
import { getFinalBodyFromRequest } from "~/helpers/utils/EffectiveURL"
export async function generateDigestAuthHeaders(
auth: HoppRESTAuth,
request: HoppRESTRequest,
envVars: Environment["variables"],
showKeyIfSecret = false
): Promise<HoppRESTHeader[]> {
if (auth.authType !== "digest") return []
const { method, endpoint } = request
// Step 1: Fetch the initial auth info (nonce, realm, etc.)
const authInfo = await fetchInitialDigestAuthInfo(
parseTemplateString(endpoint, envVars),
method
)
// Get the body content for digest calculation
const reqBody = getFinalBodyFromRequest(request, envVars, showKeyIfSecret)
// Step 2: Set up the parameters for the digest authentication header
const digestAuthParams: DigestAuthParams = {
username: parseTemplateString(auth.username, envVars),
password: parseTemplateString(auth.password, envVars),
realm: auth.realm
? parseTemplateString(auth.realm, envVars)
: authInfo.realm,
nonce: auth.nonce
? parseTemplateString(auth.nonce, envVars)
: authInfo.nonce,
endpoint: parseTemplateString(endpoint, envVars),
method,
algorithm: auth.algorithm ?? authInfo.algorithm,
qop: auth.qop ? parseTemplateString(auth.qop, envVars) : authInfo.qop,
opaque: auth.opaque
? parseTemplateString(auth.opaque, envVars)
: authInfo.opaque,
reqBody: typeof reqBody === "string" ? reqBody : "",
}
// Step 3: Generate the Authorization header
const authHeaderValue = await generateDigestAuthHeader(digestAuthParams)
return [
{
active: true,
key: "Authorization",
value: authHeaderValue,
description: "",
},
]
}

View file

@ -0,0 +1,55 @@
import {
parseTemplateString,
calculateHawkHeader,
HoppRESTAuth,
HoppRESTRequest,
Environment,
HoppRESTHeader,
HoppRESTParam,
} from "@hoppscotch/data"
import { getFinalBodyFromRequest } from "~/helpers/utils/EffectiveURL"
export async function generateHawkAuthHeaders(
auth: HoppRESTAuth,
request: HoppRESTRequest,
envVars: Environment["variables"],
showKeyIfSecret = false
): Promise<HoppRESTHeader[]> {
if (auth.authType !== "hawk") return []
const { method, endpoint, body } = request
// Get the body content for payload hash calculation
const payload = getFinalBodyFromRequest(request, envVars, showKeyIfSecret)
const hawkHeader = await calculateHawkHeader({
url: parseTemplateString(endpoint, envVars),
method: method,
id: parseTemplateString(auth.authId, envVars),
key: parseTemplateString(auth.authKey, envVars),
algorithm: auth.algorithm,
// Add content type and payload
contentType: body.contentType,
payload,
// advanced parameters (optional)
includePayloadHash: auth.includePayloadHash,
nonce: auth.nonce ? parseTemplateString(auth.nonce, envVars) : undefined,
ext: auth.ext ? parseTemplateString(auth.ext, envVars) : undefined,
app: auth.app ? parseTemplateString(auth.app, envVars) : undefined,
dlg: auth.dlg ? parseTemplateString(auth.dlg, envVars) : undefined,
timestamp: auth.timestamp
? parseInt(parseTemplateString(auth.timestamp, envVars), 10)
: undefined,
})
return [
{
active: true,
key: "Authorization",
value: hawkHeader,
description: "",
},
]
}

View file

@ -0,0 +1,78 @@
import {
parseTemplateString,
generateJWTToken,
HoppRESTAuth,
HoppRESTRequest,
Environment,
HoppRESTHeader,
HoppRESTParam,
} from "@hoppscotch/data"
export async function generateJwtAuthHeaders(
auth: HoppRESTAuth & { authType: "jwt" },
request: HoppRESTRequest,
envVars: Environment["variables"],
showKeyIfSecret = false
): Promise<HoppRESTHeader[]> {
if (auth.addTo !== "HEADERS") return []
const token = await generateJWTToken({
algorithm: auth.algorithm || "HS256",
secret: parseTemplateString(auth.secret, envVars, false),
privateKey: parseTemplateString(auth.privateKey, envVars, false),
payload: parseTemplateString(auth.payload, envVars, false),
jwtHeaders: parseTemplateString(auth.jwtHeaders, envVars, false),
isSecretBase64Encoded: auth.isSecretBase64Encoded,
})
if (!token) return []
// Get prefix (defaults to "Bearer " if not specified)
const headerPrefix = parseTemplateString(
auth.headerPrefix,
envVars,
false,
showKeyIfSecret
)
return [
{
active: true,
key: "Authorization",
value: `${headerPrefix}${token}`,
description: "",
},
]
}
export async function generateJwtAuthParams(
auth: HoppRESTAuth & { authType: "jwt" },
request: HoppRESTRequest,
envVars: Environment["variables"],
showKeyIfSecret = false
): Promise<HoppRESTParam[]> {
if (auth.addTo !== "QUERY_PARAMS") return []
const token = await generateJWTToken({
algorithm: auth.algorithm || "HS256",
secret: parseTemplateString(auth.secret, envVars, false),
privateKey: parseTemplateString(auth.privateKey, envVars, false),
payload: parseTemplateString(auth.payload, envVars, false),
jwtHeaders: parseTemplateString(auth.jwtHeaders, envVars, false),
isSecretBase64Encoded: auth.isSecretBase64Encoded,
})
if (!token) return []
// Get param name (defaults to "token" if not specified)
const paramName = parseTemplateString(auth.paramName, envVars)
return [
{
active: true,
key: paramName,
value: token,
description: "",
},
]
}

View file

@ -0,0 +1,58 @@
import {
parseTemplateString,
HoppRESTAuth,
HoppRESTRequest,
Environment,
HoppRESTHeader,
HoppRESTParam,
} from "@hoppscotch/data"
export async function generateOAuth2AuthHeaders(
auth: HoppRESTAuth & { authType: "oauth-2" },
request: HoppRESTRequest,
envVars: Environment["variables"],
showKeyIfSecret = false
): Promise<HoppRESTHeader[]> {
if (auth.addTo !== "HEADERS") return []
const token = parseTemplateString(
auth.grantTypeInfo.token,
envVars,
false,
showKeyIfSecret
)
return [
{
active: true,
key: "Authorization",
value: `Bearer ${token}`,
description: "",
},
]
}
export async function generateOAuth2AuthParams(
auth: HoppRESTAuth & { authType: "oauth-2" },
request: HoppRESTRequest,
envVars: Environment["variables"],
showKeyIfSecret = false
): Promise<HoppRESTParam[]> {
if (auth.addTo !== "QUERY_PARAMS") return []
const token = parseTemplateString(
auth.grantTypeInfo.token,
envVars,
false,
showKeyIfSecret
)
return [
{
active: true,
key: "access_token",
value: token,
description: "",
},
]
}

View file

@ -13,7 +13,6 @@ import {
parseTemplateString, parseTemplateString,
parseTemplateStringE, parseTemplateStringE,
} from "@hoppscotch/data" } from "@hoppscotch/data"
import { AwsV4Signer } from "aws4fetch"
import * as A from "fp-ts/Array" import * as A from "fp-ts/Array"
import * as E from "fp-ts/Either" import * as E from "fp-ts/Either"
import { flow, pipe } from "fp-ts/function" import { flow, pipe } from "fp-ts/function"
@ -29,12 +28,7 @@ import { toFormData } from "../functional/formData"
import { tupleWithSameKeysToRecord } from "../functional/record" import { tupleWithSameKeysToRecord } from "../functional/record"
import { isJSONContentType } from "./contenttypes" import { isJSONContentType } from "./contenttypes"
import { stripComments } from "../editor/linting/jsonc" import { stripComments } from "../editor/linting/jsonc"
import { import { generateAuthHeaders, generateAuthParams } from "../auth/auth-types"
DigestAuthParams,
fetchInitialDigestAuthInfo,
generateDigestAuthHeader,
} from "../auth/digest"
import { calculateHawkHeader, generateJWTToken } from "@hoppscotch/data"
export interface EffectiveHoppRESTRequest extends HoppRESTRequest { export interface EffectiveHoppRESTRequest extends HoppRESTRequest {
/** /**
@ -72,262 +66,14 @@ export const getComputedAuthHeaders = async (
) => { ) => {
const request = auth ? { auth: auth ?? { authActive: false } } : req const request = auth ? { auth: auth ?? { authActive: false } } : req
/** if (!request || !request.auth || !request.auth.authActive) return []
* Handling Authorization header priority rules:
*
* 1. If a user-defined "Authorization" header exists in the request:
* a. We generally give it priority over auth-generated headers
* b. EXCEPTION: API Key auth that uses a different header name should still be included
*
* 2. We need to check both:
* - req.auth (the current request's auth settings)
* - auth param (possibly inherited auth from a parent collection)
*
* 3. Only return empty array (blocking auth headers) when:
* - Neither req.auth nor auth param is using API Key auth, OR
* - API Key auth is being used but specifically with the "Authorization" header name
* - This prevents API Key auth from being blocked when using custom header names
*/
if (req && req.headers.find((h) => h.key.toLowerCase() === "authorization")) {
// Only return empty array if not using API key auth or if API key is using "authorization" header
if (
(!req.auth ||
req.auth.authType !== "api-key" ||
req.auth.key.toLowerCase() === "authorization") &&
(!auth ||
auth.authType !== "api-key" ||
auth.key.toLowerCase() === "authorization")
) {
return []
}
}
if (!request) return [] return await generateAuthHeaders(
request.auth,
if (!request.auth || !request.auth.authActive) return [] req as HoppRESTRequest,
envVars,
const headers: HoppRESTHeader[] = [] showKeyIfSecret
)
// TODO: Support a better b64 implementation than btoa ?
if (request.auth.authType === "basic") {
const username = parse
? parseTemplateString(
request.auth.username,
envVars,
false,
showKeyIfSecret
)
: request.auth.username
const password = parse
? parseTemplateString(
request.auth.password,
envVars,
false,
showKeyIfSecret
)
: request.auth.password
headers.push({
active: true,
key: "Authorization",
value: `Basic ${btoa(`${username}:${password}`)}`,
description: "",
})
} else if (request.auth.authType === "digest") {
const { method, endpoint } = request as HoppRESTRequest
// Step 1: Fetch the initial auth info (nonce, realm, etc.)
const authInfo = await fetchInitialDigestAuthInfo(
parseTemplateString(endpoint, envVars),
method
)
const reqBody = getFinalBodyFromRequest(
req as HoppRESTRequest,
envVars,
showKeyIfSecret
)
// Step 2: Set up the parameters for the digest authentication header
const digestAuthParams: DigestAuthParams = {
username: parseTemplateString(request.auth.username, envVars),
password: parseTemplateString(request.auth.password, envVars),
realm: request.auth.realm
? parseTemplateString(request.auth.realm, envVars)
: authInfo.realm,
nonce: request.auth.nonce
? parseTemplateString(authInfo.nonce, envVars)
: authInfo.nonce,
endpoint: parseTemplateString(endpoint, envVars),
method,
algorithm: request.auth.algorithm ?? authInfo.algorithm,
qop: request.auth.qop
? parseTemplateString(request.auth.qop, envVars)
: authInfo.qop,
opaque: request.auth.opaque
? parseTemplateString(request.auth.opaque, envVars)
: authInfo.opaque,
reqBody: typeof reqBody === "string" ? reqBody : "",
}
// Step 3: Generate the Authorization header
const authHeaderValue = await generateDigestAuthHeader(digestAuthParams)
headers.push({
active: true,
key: "Authorization",
value: authHeaderValue,
description: "",
})
} else if (
request.auth.authType === "bearer" ||
(request.auth.authType === "oauth-2" && request.auth.addTo === "HEADERS")
) {
const token =
request.auth.authType === "bearer"
? request.auth.token
: request.auth.grantTypeInfo.token
headers.push({
active: true,
key: "Authorization",
value: `Bearer ${
parse
? parseTemplateString(token, envVars, false, showKeyIfSecret)
: token
}`,
description: "",
})
} else if (request.auth.authType === "api-key") {
const { key, addTo } = request.auth
if (addTo === "HEADERS" && key) {
headers.push({
active: true,
key: parseTemplateString(key, envVars, false, showKeyIfSecret),
value: parse
? parseTemplateString(
request.auth.value ?? "",
envVars,
false,
showKeyIfSecret
)
: (request.auth.value ?? ""),
description: "",
})
}
} else if (request.auth.authType === "aws-signature") {
const { addTo } = request.auth
if (addTo === "HEADERS") {
const currentDate = new Date()
const amzDate = currentDate.toISOString().replace(/[:-]|\.\d{3}/g, "")
const { method, endpoint } = req as HoppRESTRequest
const body = getFinalBodyFromRequest(request, envVars)
const signer = new AwsV4Signer({
method: method,
body: body?.toString(),
datetime: amzDate,
accessKeyId: parseTemplateString(request.auth.accessKey, envVars),
secretAccessKey: parseTemplateString(request.auth.secretKey, envVars),
region:
parseTemplateString(request.auth.region, envVars) ?? "us-east-1",
service: parseTemplateString(request.auth.serviceName, envVars),
sessionToken:
request.auth.serviceToken &&
parseTemplateString(request.auth.serviceToken, envVars),
url: parseTemplateString(endpoint, envVars),
})
const sign = await signer.sign()
sign.headers.forEach((x, k) => {
headers.push({
active: true,
key: k,
value: x,
description: "",
})
})
}
} else if (request.auth.authType === "hawk") {
const { method, endpoint, body } = req as HoppRESTRequest
// Get the body content for payload hash calculation
const payload = getFinalBodyFromRequest(
req as HoppRESTRequest,
envVars,
showKeyIfSecret
)
const hawkHeader = await calculateHawkHeader({
url: parseTemplateString(endpoint, envVars), // URL
method: method, // HTTP method
id: parseTemplateString(request.auth.authId, envVars),
key: parseTemplateString(request.auth.authKey, envVars),
algorithm: request.auth.algorithm,
// Add content type and payload
contentType: body.contentType,
payload,
// advanced parameters (optional)
includePayloadHash: request.auth.includePayloadHash,
nonce: request.auth.nonce
? parseTemplateString(request.auth.nonce, envVars)
: undefined,
ext: request.auth.ext
? parseTemplateString(request.auth.ext, envVars)
: undefined,
app: request.auth.app
? parseTemplateString(request.auth.app, envVars)
: undefined,
dlg: request.auth.dlg
? parseTemplateString(request.auth.dlg, envVars)
: undefined,
timestamp: request.auth.timestamp
? parseInt(parseTemplateString(request.auth.timestamp, envVars), 10)
: undefined,
})
headers.push({
active: true,
key: "Authorization",
value: hawkHeader,
description: "",
})
} else if (
request.auth.authType === "jwt" &&
request.auth.addTo === "HEADERS"
) {
const token = await generateJWTToken({
algorithm: request.auth.algorithm || "HS256",
secret: parseTemplateString(request.auth.secret, envVars, false),
privateKey: parseTemplateString(request.auth.privateKey, envVars, false),
payload: parseTemplateString(request.auth.payload, envVars, false),
jwtHeaders: parseTemplateString(request.auth.jwtHeaders, envVars, false),
isSecretBase64Encoded: request.auth.isSecretBase64Encoded,
})
if (token) {
// Get prefix (defaults to "Bearer " if not specified)
const headerPrefix = parseTemplateString(
request.auth.headerPrefix,
envVars,
false,
showKeyIfSecret
)
headers.push({
active: true,
key: "Authorization",
value: `${headerPrefix}${token}`,
description: "",
})
}
}
return headers
} }
/** /**
@ -453,116 +199,10 @@ export const getComputedParams = async (
req: HoppRESTRequest, req: HoppRESTRequest,
envVars: Environment["variables"] envVars: Environment["variables"]
): Promise<ComputedParam[]> => { ): Promise<ComputedParam[]> => {
// When this gets complex, its best to split this function off (like with getComputedHeaders)
// API-key auth can be added to query params
if (!req.auth || !req.auth.authActive) return [] if (!req.auth || !req.auth.authActive) return []
if ( const params = await generateAuthParams(req.auth, req, envVars)
req.auth.authType !== "api-key" && return params.map((param) => ({ source: "auth" as const, param }))
req.auth.authType !== "oauth-2" &&
req.auth.authType !== "aws-signature" &&
req.auth.authType !== "jwt"
)
return []
if (req.auth.addTo !== "QUERY_PARAMS") return []
if (req.auth.authType === "aws-signature") {
const { addTo } = req.auth
const params: ComputedParam[] = []
if (addTo === "QUERY_PARAMS") {
const currentDate = new Date()
const amzDate = currentDate.toISOString().replace(/[:-]|\.\d{3}/g, "")
const signer = new AwsV4Signer({
method: req.method,
datetime: amzDate,
signQuery: true,
accessKeyId: parseTemplateString(req.auth.accessKey, envVars),
secretAccessKey: parseTemplateString(req.auth.secretKey, envVars),
region: parseTemplateString(req.auth.region, envVars) ?? "us-east-1",
service: parseTemplateString(req.auth.serviceName, envVars),
sessionToken:
req.auth.serviceToken &&
parseTemplateString(req.auth.serviceToken, envVars),
url: parseTemplateString(req.endpoint, envVars),
})
const sign = await signer.sign()
for (const [k, v] of sign.url.searchParams) {
params.push({
source: "auth" as const,
param: {
active: true,
key: k,
value: v,
description: "",
},
})
}
}
return params
}
if (req.auth.authType === "api-key") {
return [
{
source: "auth" as const,
param: {
active: true,
key: parseTemplateString(req.auth.key, envVars, false, true),
value: parseTemplateString(req.auth.value, envVars, false, true),
description: "",
},
},
]
}
if (req.auth.authType === "oauth-2") {
const { grantTypeInfo } = req.auth
return [
{
source: "auth",
param: {
active: true,
key: "access_token",
value: parseTemplateString(grantTypeInfo.token, envVars),
description: "",
},
},
]
}
if (req.auth.authType === "jwt") {
const token = await generateJWTToken({
algorithm: req.auth.algorithm || "HS256",
secret: parseTemplateString(req.auth.secret, envVars, false),
privateKey: parseTemplateString(req.auth.privateKey, envVars, false),
payload: parseTemplateString(req.auth.payload, envVars, false),
jwtHeaders: parseTemplateString(req.auth.jwtHeaders, envVars, false),
isSecretBase64Encoded: req.auth.isSecretBase64Encoded,
})
if (token) {
// Get param name (defaults to "token" if not specified)
const paramName = parseTemplateString(req.auth.paramName, envVars)
return [
{
source: "auth",
param: {
active: true,
key: paramName,
value: token,
description: "",
},
},
]
}
return []
}
return []
} }
// Resolves environment variables in the body // Resolves environment variables in the body
@ -616,7 +256,7 @@ export const resolvesEnvsInBody = (
} }
} }
function getFinalBodyFromRequest( export function getFinalBodyFromRequest(
request: HoppRESTRequest, request: HoppRESTRequest,
envVariables: Environment["variables"], envVariables: Environment["variables"],
showKeyIfSecret = false showKeyIfSecret = false

View file

@ -34,7 +34,10 @@ const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename) const __dirname = path.dirname(__filename)
// Define the directory paths and file patterns to perform type checks on // Define the directory paths and file patterns to perform type checks on
const directoryPaths = [path.resolve(__dirname, "src", "services")] const directoryPaths = [
path.resolve(__dirname, "src", "services"),
path.resolve(__dirname, "src", "helpers", "auth"),
]
const filePatterns = ["**/*.ts"] const filePatterns = ["**/*.ts"]
const tsConfigFileName = path.resolve(__dirname, "tsconfig.json") const tsConfigFileName = path.resolve(__dirname, "tsconfig.json")