test(common): add comprehensive unit tests for auth helpers (#5211)
Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
parent
ed8b85bb67
commit
4f393d37a5
15 changed files with 1559 additions and 51 deletions
|
|
@ -34,15 +34,15 @@ export async function generateAuthHeaders(
|
|||
): Promise<HoppRESTHeader[]> {
|
||||
switch (auth.authType) {
|
||||
case "basic":
|
||||
return generateBasicAuthHeaders(auth, request, envVars, showKeyIfSecret)
|
||||
return generateBasicAuthHeaders(auth, envVars, showKeyIfSecret)
|
||||
case "bearer":
|
||||
return generateBearerAuthHeaders(auth, request, envVars, showKeyIfSecret)
|
||||
return generateBearerAuthHeaders(auth, envVars, showKeyIfSecret)
|
||||
case "api-key":
|
||||
return auth.addTo === "HEADERS"
|
||||
? generateApiKeyAuthHeaders(auth, request, envVars, showKeyIfSecret)
|
||||
? generateApiKeyAuthHeaders(auth, envVars, showKeyIfSecret)
|
||||
: []
|
||||
case "oauth-2":
|
||||
return generateOAuth2AuthHeaders(auth, request, envVars, showKeyIfSecret)
|
||||
return generateOAuth2AuthHeaders(auth, envVars, showKeyIfSecret)
|
||||
case "digest":
|
||||
return generateDigestAuthHeaders(auth, request, envVars, showKeyIfSecret)
|
||||
case "aws-signature":
|
||||
|
|
@ -50,7 +50,7 @@ export async function generateAuthHeaders(
|
|||
case "hawk":
|
||||
return generateHawkAuthHeaders(auth, request, envVars, showKeyIfSecret)
|
||||
case "jwt":
|
||||
return generateJwtAuthHeaders(auth, request, envVars, showKeyIfSecret)
|
||||
return generateJwtAuthHeaders(auth, envVars, showKeyIfSecret)
|
||||
default:
|
||||
return []
|
||||
}
|
||||
|
|
@ -68,14 +68,14 @@ export async function generateAuthParams(
|
|||
switch (auth.authType) {
|
||||
case "api-key":
|
||||
return auth.addTo === "QUERY_PARAMS"
|
||||
? generateApiKeyAuthParams(auth, request, envVars, showKeyIfSecret)
|
||||
? generateApiKeyAuthParams(auth, envVars, showKeyIfSecret)
|
||||
: []
|
||||
case "oauth-2":
|
||||
return generateOAuth2AuthParams(auth, request, envVars, showKeyIfSecret)
|
||||
return generateOAuth2AuthParams(auth, envVars, showKeyIfSecret)
|
||||
case "aws-signature":
|
||||
return generateAwsSignatureAuthParams(auth, request, envVars)
|
||||
case "jwt":
|
||||
return generateJwtAuthParams(auth, request, envVars)
|
||||
return generateJwtAuthParams(auth, envVars)
|
||||
default:
|
||||
return []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
import { HoppRESTAuth } from "@hoppscotch/data"
|
||||
import { describe, expect, test } from "vitest"
|
||||
import { generateApiKeyAuthHeaders, generateApiKeyAuthParams } from "../api-key"
|
||||
import { mockEnvVars } from "./test-utils"
|
||||
|
||||
describe("API Key Auth", () => {
|
||||
describe("generateApiKeyAuthHeaders", () => {
|
||||
test("generates headers when addTo is HEADERS", async () => {
|
||||
const auth: HoppRESTAuth & { authType: "api-key" } = {
|
||||
authActive: true,
|
||||
authType: "api-key",
|
||||
addTo: "HEADERS",
|
||||
key: "X-API-Key",
|
||||
value: "<<API_VALUE>>",
|
||||
}
|
||||
|
||||
const headers = await generateApiKeyAuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(headers).toHaveLength(1)
|
||||
expect(headers[0]).toEqual({
|
||||
active: true,
|
||||
key: "X-API-Key",
|
||||
value: "secret-value",
|
||||
description: "",
|
||||
})
|
||||
})
|
||||
|
||||
test("returns empty array when addTo is not HEADERS", async () => {
|
||||
const auth: HoppRESTAuth & { authType: "api-key" } = {
|
||||
authActive: true,
|
||||
authType: "api-key",
|
||||
addTo: "QUERY_PARAMS",
|
||||
key: "api_key",
|
||||
value: "test-value",
|
||||
}
|
||||
|
||||
const headers = await generateApiKeyAuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(headers).toHaveLength(0)
|
||||
})
|
||||
|
||||
test("handles template strings in key and value", async () => {
|
||||
const auth: HoppRESTAuth & { authType: "api-key" } = {
|
||||
authActive: true,
|
||||
authType: "api-key",
|
||||
addTo: "HEADERS",
|
||||
key: "<<API_KEY>>",
|
||||
value: "<<API_VALUE>>",
|
||||
}
|
||||
|
||||
const headers = await generateApiKeyAuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(headers[0].key).toBe("test-key-123")
|
||||
expect(headers[0].value).toBe("secret-value")
|
||||
})
|
||||
})
|
||||
|
||||
describe("generateApiKeyAuthParams", () => {
|
||||
test("generates params when addTo is QUERY_PARAMS", async () => {
|
||||
const auth: HoppRESTAuth & { authType: "api-key" } = {
|
||||
authActive: true,
|
||||
authType: "api-key",
|
||||
addTo: "QUERY_PARAMS",
|
||||
key: "api_key",
|
||||
value: "<<API_VALUE>>",
|
||||
}
|
||||
|
||||
const params = await generateApiKeyAuthParams(auth, mockEnvVars)
|
||||
|
||||
expect(params).toHaveLength(1)
|
||||
expect(params[0]).toEqual({
|
||||
active: true,
|
||||
key: "api_key",
|
||||
value: "secret-value",
|
||||
description: "",
|
||||
})
|
||||
})
|
||||
|
||||
test("returns empty array when addTo is not QUERY_PARAMS", async () => {
|
||||
const auth: HoppRESTAuth & { authType: "api-key" } = {
|
||||
authActive: true,
|
||||
authType: "api-key",
|
||||
addTo: "HEADERS",
|
||||
key: "X-API-Key",
|
||||
value: "test-value",
|
||||
}
|
||||
|
||||
const params = await generateApiKeyAuthParams(auth, mockEnvVars)
|
||||
|
||||
expect(params).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import { describe, expect, test, vi, beforeEach, afterEach } from "vitest"
|
||||
import { makeRESTRequest } from "@hoppscotch/data"
|
||||
import type { Environment, HoppRESTAuth } from "@hoppscotch/data"
|
||||
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"
|
||||
import {
|
||||
generateAwsSignatureAuthHeaders,
|
||||
generateAwsSignatureAuthParams,
|
||||
} from "../aws-signature"
|
||||
import type { HoppRESTAuth, Environment } from "@hoppscotch/data"
|
||||
import { createBaseRequest } from "./test-utils"
|
||||
|
||||
vi.mock("aws4fetch", () => ({
|
||||
AwsV4Signer: vi.fn().mockImplementation((config) => ({
|
||||
|
|
@ -69,33 +69,6 @@ describe("AWS Signature Auth", () => {
|
|||
...overrides,
|
||||
})
|
||||
|
||||
// Helper function to create base request
|
||||
const createBaseRequest = (
|
||||
overrides: Partial<Parameters<typeof makeRESTRequest>[0]> = {}
|
||||
) => {
|
||||
const baseRequest: Parameters<typeof makeRESTRequest>[0] = {
|
||||
method: "GET",
|
||||
endpoint: "https://s3.amazonaws.com/bucket/key",
|
||||
name: "Test Request",
|
||||
params: [],
|
||||
headers: [],
|
||||
preRequestScript: "",
|
||||
testScript: "",
|
||||
auth: {
|
||||
authType: "inherit",
|
||||
authActive: true,
|
||||
},
|
||||
body: {
|
||||
contentType: null,
|
||||
body: null,
|
||||
},
|
||||
requestVariables: [],
|
||||
responses: {},
|
||||
}
|
||||
|
||||
return makeRESTRequest({ ...baseRequest, ...overrides })
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
vi.useFakeTimers()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
import { HoppRESTAuth } from "@hoppscotch/data"
|
||||
import { describe, expect, test } from "vitest"
|
||||
import { generateBasicAuthHeaders } from "../basic"
|
||||
import { mockEnvVars } from "./test-utils"
|
||||
|
||||
describe("Basic Auth", () => {
|
||||
describe("generateBasicAuthHeaders", () => {
|
||||
test("generates basic auth header with credentials", async () => {
|
||||
const auth: HoppRESTAuth & { authType: "basic" } = {
|
||||
authActive: true,
|
||||
authType: "basic",
|
||||
username: "admin",
|
||||
password: "secret123",
|
||||
}
|
||||
|
||||
const headers = await generateBasicAuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(headers).toHaveLength(1)
|
||||
expect(headers[0]).toEqual({
|
||||
active: true,
|
||||
key: "Authorization",
|
||||
value: `Basic ${btoa("admin:secret123")}`,
|
||||
description: "",
|
||||
})
|
||||
})
|
||||
|
||||
test("handles template strings in username and password", async () => {
|
||||
const auth: HoppRESTAuth & { authType: "basic" } = {
|
||||
authActive: true,
|
||||
authType: "basic",
|
||||
username: "<<USERNAME>>",
|
||||
password: "<<PASSWORD>>",
|
||||
}
|
||||
|
||||
const headers = await generateBasicAuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(headers[0].value).toBe(`Basic ${btoa("testuser:testpass")}`)
|
||||
})
|
||||
|
||||
test("handles empty credentials", async () => {
|
||||
const auth: HoppRESTAuth & { authType: "basic" } = {
|
||||
authActive: true,
|
||||
authType: "basic",
|
||||
username: "",
|
||||
password: "",
|
||||
}
|
||||
|
||||
const headers = await generateBasicAuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(headers[0].value).toBe(`Basic ${btoa(":")}`)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
import { describe, test, expect } from "vitest"
|
||||
import { generateBearerAuthHeaders } from "../bearer"
|
||||
import { createBaseRequest, mockEnvVars } from "./test-utils"
|
||||
import { HoppRESTAuth } from "@hoppscotch/data"
|
||||
|
||||
describe("Bearer Auth", () => {
|
||||
describe("generateBearerAuthHeaders", () => {
|
||||
test("generates bearer auth header with token", async () => {
|
||||
const auth: HoppRESTAuth & { authType: "bearer" } = {
|
||||
authActive: true,
|
||||
authType: "bearer",
|
||||
token: "abc123token",
|
||||
}
|
||||
|
||||
const headers = await generateBearerAuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(headers).toHaveLength(1)
|
||||
expect(headers[0]).toEqual({
|
||||
active: true,
|
||||
key: "Authorization",
|
||||
value: "Bearer abc123token",
|
||||
description: "",
|
||||
})
|
||||
})
|
||||
|
||||
test("handles template strings in token", async () => {
|
||||
const auth: HoppRESTAuth & { authType: "bearer" } = {
|
||||
authActive: true,
|
||||
authType: "bearer",
|
||||
token: "<<ACCESS_TOKEN>>",
|
||||
}
|
||||
|
||||
const headers = await generateBearerAuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(headers[0].value).toBe(
|
||||
"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
|
||||
)
|
||||
})
|
||||
|
||||
test("handles empty token", async () => {
|
||||
const auth: HoppRESTAuth & { authType: "bearer" } = {
|
||||
authActive: true,
|
||||
authType: "bearer",
|
||||
token: "",
|
||||
}
|
||||
|
||||
const headers = await generateBearerAuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(headers[0].value).toBe("Bearer ")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,557 @@
|
|||
import { describe, test, expect, vi, beforeEach } from "vitest"
|
||||
import { generateDigestAuthHeaders } from "../digest"
|
||||
import { createBaseRequest, mockEnvVars } from "./test-utils"
|
||||
import { HoppRESTAuth } from "@hoppscotch/data"
|
||||
|
||||
// Mock the digest helper functions
|
||||
vi.mock("~/helpers/auth/digest", () => ({
|
||||
generateDigestAuthHeader: vi.fn(),
|
||||
fetchInitialDigestAuthInfo: vi.fn(),
|
||||
}))
|
||||
|
||||
import {
|
||||
generateDigestAuthHeader,
|
||||
fetchInitialDigestAuthInfo,
|
||||
} from "~/helpers/auth/digest"
|
||||
|
||||
describe("Digest Auth", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
|
||||
// Set up default mocks for fetchInitialDigestAuthInfo
|
||||
vi.mocked(fetchInitialDigestAuthInfo).mockResolvedValue({
|
||||
realm: "Default Realm",
|
||||
nonce: "default-nonce",
|
||||
qop: "auth",
|
||||
algorithm: "MD5",
|
||||
opaque: "",
|
||||
})
|
||||
})
|
||||
|
||||
describe("generateDigestAuthHeaders", () => {
|
||||
test("generates digest auth header with basic configuration", async () => {
|
||||
const mockDigestInfo = {
|
||||
realm: "Protected Area",
|
||||
nonce: "abc123",
|
||||
qop: "auth",
|
||||
algorithm: "MD5",
|
||||
nc: "00000001",
|
||||
cnonce: "",
|
||||
opaque: "",
|
||||
}
|
||||
|
||||
const mockDigestHeader =
|
||||
'Digest username="testuser", realm="Protected Area", nonce="abc123", uri="/api/data", algorithm="MD5", response="def456", qop=auth, nc=00000001, cnonce="xyz789"'
|
||||
|
||||
vi.mocked(fetchInitialDigestAuthInfo).mockResolvedValue(mockDigestInfo)
|
||||
vi.mocked(generateDigestAuthHeader).mockResolvedValue(mockDigestHeader)
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "digest" } = {
|
||||
authActive: true,
|
||||
authType: "digest",
|
||||
username: "testuser",
|
||||
password: "testpass",
|
||||
realm: "Protected Area",
|
||||
nonce: "abc123",
|
||||
algorithm: "MD5",
|
||||
qop: "auth",
|
||||
nc: "00000001",
|
||||
cnonce: "",
|
||||
opaque: "",
|
||||
disableRetry: false,
|
||||
}
|
||||
|
||||
const headers = await generateDigestAuthHeaders(
|
||||
auth,
|
||||
createBaseRequest(),
|
||||
mockEnvVars
|
||||
)
|
||||
|
||||
expect(generateDigestAuthHeader).toHaveBeenCalledWith({
|
||||
username: "testuser",
|
||||
password: "testpass",
|
||||
realm: "Protected Area",
|
||||
nonce: "abc123",
|
||||
endpoint: "https://api.example.com/data",
|
||||
method: "GET",
|
||||
algorithm: "MD5",
|
||||
qop: "auth",
|
||||
opaque: "",
|
||||
reqBody: "",
|
||||
})
|
||||
|
||||
expect(headers).toHaveLength(1)
|
||||
expect(headers[0]).toEqual({
|
||||
active: true,
|
||||
key: "Authorization",
|
||||
value: mockDigestHeader,
|
||||
description: "",
|
||||
})
|
||||
})
|
||||
|
||||
test("handles MD5-sess algorithm", async () => {
|
||||
const mockDigestInfo = {
|
||||
realm: "Test",
|
||||
nonce: "nonce123",
|
||||
qop: "auth",
|
||||
algorithm: "MD5-sess",
|
||||
nc: "00000001",
|
||||
cnonce: "",
|
||||
opaque: "",
|
||||
}
|
||||
|
||||
const mockDigestHeader =
|
||||
'Digest username="user", realm="Test", nonce="nonce123", uri="/api", algorithm="MD5-sess", response="response456", qop=auth, nc=00000001, cnonce="client789"'
|
||||
|
||||
vi.mocked(fetchInitialDigestAuthInfo).mockResolvedValue(mockDigestInfo)
|
||||
vi.mocked(generateDigestAuthHeader).mockResolvedValue(mockDigestHeader)
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "digest" } = {
|
||||
authActive: true,
|
||||
authType: "digest",
|
||||
username: "user",
|
||||
password: "pass",
|
||||
realm: "Test",
|
||||
nonce: "nonce123",
|
||||
algorithm: "MD5-sess",
|
||||
qop: "auth",
|
||||
nc: "00000001",
|
||||
cnonce: "",
|
||||
opaque: "",
|
||||
disableRetry: false,
|
||||
}
|
||||
|
||||
const headers = await generateDigestAuthHeaders(
|
||||
auth,
|
||||
createBaseRequest(),
|
||||
mockEnvVars
|
||||
)
|
||||
|
||||
expect(generateDigestAuthHeader).toHaveBeenCalledWith({
|
||||
username: "user",
|
||||
password: "pass",
|
||||
realm: "Test",
|
||||
nonce: "nonce123",
|
||||
endpoint: "https://api.example.com/data",
|
||||
method: "GET",
|
||||
algorithm: "MD5-sess",
|
||||
qop: "auth",
|
||||
opaque: "",
|
||||
reqBody: "",
|
||||
})
|
||||
|
||||
expect(headers[0].value).toBe(mockDigestHeader)
|
||||
})
|
||||
|
||||
test("handles auth-int qop with request body", async () => {
|
||||
const mockDigestHeader =
|
||||
'Digest username="user", realm="Protected", nonce="nonce456", uri="/api/update", algorithm="MD5", response="response789", qop=auth-int, nc=00000001, cnonce="client123"'
|
||||
|
||||
vi.mocked(generateDigestAuthHeader).mockResolvedValue(mockDigestHeader)
|
||||
|
||||
const requestWithBody = createBaseRequest({
|
||||
method: "POST",
|
||||
body: {
|
||||
contentType: "application/json" as const,
|
||||
body: '{"name": "test", "value": 123}',
|
||||
},
|
||||
})
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "digest" } = {
|
||||
authActive: true,
|
||||
authType: "digest",
|
||||
username: "user",
|
||||
password: "pass",
|
||||
realm: "Protected",
|
||||
nonce: "nonce456",
|
||||
algorithm: "MD5",
|
||||
qop: "auth-int",
|
||||
nc: "00000001",
|
||||
cnonce: "",
|
||||
opaque: "",
|
||||
disableRetry: false,
|
||||
}
|
||||
|
||||
const headers = await generateDigestAuthHeaders(
|
||||
auth,
|
||||
requestWithBody,
|
||||
mockEnvVars
|
||||
)
|
||||
|
||||
expect(generateDigestAuthHeader).toHaveBeenCalledWith({
|
||||
username: "user",
|
||||
password: "pass",
|
||||
realm: "Protected",
|
||||
nonce: "nonce456",
|
||||
endpoint: "https://api.example.com/data",
|
||||
method: "POST",
|
||||
algorithm: "MD5",
|
||||
qop: "auth-int",
|
||||
opaque: "",
|
||||
reqBody: '{"name": "test", "value": 123}',
|
||||
})
|
||||
|
||||
expect(headers[0].value).toBe(mockDigestHeader)
|
||||
})
|
||||
|
||||
test("handles template variables in username and password", async () => {
|
||||
const mockDigestHeader =
|
||||
'Digest username="testuser", realm="realm", nonce="nonce", uri="/api/data", algorithm="MD5", response="response", qop=auth, nc=00000001, cnonce="cnonce"'
|
||||
|
||||
vi.mocked(generateDigestAuthHeader).mockResolvedValue(mockDigestHeader)
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "digest" } = {
|
||||
authActive: true,
|
||||
authType: "digest",
|
||||
username: "<<DIGEST_USER>>",
|
||||
password: "<<DIGEST_PASS>>",
|
||||
realm: "realm",
|
||||
nonce: "nonce",
|
||||
algorithm: "MD5",
|
||||
qop: "auth",
|
||||
nc: "00000001",
|
||||
cnonce: "",
|
||||
opaque: "",
|
||||
disableRetry: false,
|
||||
}
|
||||
|
||||
const headers = await generateDigestAuthHeaders(
|
||||
auth,
|
||||
createBaseRequest(),
|
||||
mockEnvVars
|
||||
)
|
||||
|
||||
expect(generateDigestAuthHeader).toHaveBeenCalledWith({
|
||||
username: "testuser",
|
||||
password: "testpass",
|
||||
realm: "realm",
|
||||
nonce: "nonce",
|
||||
endpoint: "https://api.example.com/data",
|
||||
method: "GET",
|
||||
algorithm: "MD5",
|
||||
qop: "auth",
|
||||
opaque: "",
|
||||
reqBody: "",
|
||||
})
|
||||
|
||||
expect(headers[0].value).toBe(mockDigestHeader)
|
||||
})
|
||||
|
||||
test("handles opaque value", async () => {
|
||||
const mockDigestHeader =
|
||||
'Digest username="user", realm="Protected", nonce="nonce123", uri="/api/data", algorithm="MD5", response="response456", qop=auth, nc=00000001, cnonce="cnonce789", opaque="opaque-value-123"'
|
||||
|
||||
vi.mocked(generateDigestAuthHeader).mockResolvedValue(mockDigestHeader)
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "digest" } = {
|
||||
authActive: true,
|
||||
authType: "digest",
|
||||
username: "user",
|
||||
password: "pass",
|
||||
realm: "Protected",
|
||||
nonce: "nonce123",
|
||||
algorithm: "MD5",
|
||||
qop: "auth",
|
||||
nc: "00000001",
|
||||
cnonce: "cnonce789",
|
||||
opaque: "opaque-value-123",
|
||||
disableRetry: false,
|
||||
}
|
||||
|
||||
const headers = await generateDigestAuthHeaders(
|
||||
auth,
|
||||
createBaseRequest(),
|
||||
mockEnvVars
|
||||
)
|
||||
|
||||
expect(generateDigestAuthHeader).toHaveBeenCalledWith({
|
||||
username: "user",
|
||||
password: "pass",
|
||||
realm: "Protected",
|
||||
nonce: "nonce123",
|
||||
endpoint: "https://api.example.com/data",
|
||||
method: "GET",
|
||||
algorithm: "MD5",
|
||||
qop: "auth",
|
||||
opaque: "opaque-value-123",
|
||||
reqBody: "",
|
||||
})
|
||||
|
||||
expect(headers[0].value).toBe(mockDigestHeader)
|
||||
})
|
||||
|
||||
test("auto-generates cnonce when not provided", async () => {
|
||||
const mockDigestHeader =
|
||||
'Digest username="user", realm="realm", nonce="nonce", uri="/api/data", algorithm="MD5", response="response", qop=auth, nc=00000001, cnonce="auto-generated"'
|
||||
|
||||
vi.mocked(generateDigestAuthHeader).mockResolvedValue(mockDigestHeader)
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "digest" } = {
|
||||
authActive: true,
|
||||
authType: "digest",
|
||||
username: "user",
|
||||
password: "pass",
|
||||
realm: "realm",
|
||||
nonce: "nonce",
|
||||
algorithm: "MD5",
|
||||
qop: "auth",
|
||||
nc: "00000001",
|
||||
cnonce: "",
|
||||
opaque: "",
|
||||
disableRetry: false,
|
||||
}
|
||||
|
||||
const headers = await generateDigestAuthHeaders(
|
||||
auth,
|
||||
createBaseRequest(),
|
||||
mockEnvVars
|
||||
)
|
||||
|
||||
expect(generateDigestAuthHeader).toHaveBeenCalledWith({
|
||||
username: "user",
|
||||
password: "pass",
|
||||
realm: "realm",
|
||||
nonce: "nonce",
|
||||
endpoint: "https://api.example.com/data",
|
||||
method: "GET",
|
||||
algorithm: "MD5",
|
||||
qop: "auth",
|
||||
opaque: "",
|
||||
reqBody: "",
|
||||
})
|
||||
|
||||
expect(headers[0].value).toBe(mockDigestHeader)
|
||||
})
|
||||
|
||||
test("handles initial digest auth info fetch", async () => {
|
||||
const mockDigestInfo = {
|
||||
realm: "Fetched Realm",
|
||||
nonce: "fetched-nonce-123",
|
||||
qop: "auth",
|
||||
algorithm: "MD5",
|
||||
opaque: "fetched-opaque",
|
||||
}
|
||||
|
||||
const mockDigestHeader =
|
||||
'Digest username="user", realm="Fetched Realm", nonce="fetched-nonce-123", uri="/api/data", algorithm="MD5", response="response", qop=auth, nc=00000001, cnonce="cnonce"'
|
||||
|
||||
vi.mocked(fetchInitialDigestAuthInfo).mockResolvedValue(mockDigestInfo)
|
||||
vi.mocked(generateDigestAuthHeader).mockResolvedValue(mockDigestHeader)
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "digest" } = {
|
||||
authActive: true,
|
||||
authType: "digest",
|
||||
username: "user",
|
||||
password: "pass",
|
||||
realm: "", // Empty realm should trigger fetch
|
||||
nonce: "",
|
||||
algorithm: "MD5",
|
||||
qop: "auth",
|
||||
nc: "00000001",
|
||||
cnonce: "",
|
||||
opaque: "",
|
||||
disableRetry: false,
|
||||
}
|
||||
|
||||
const headers = await generateDigestAuthHeaders(
|
||||
auth,
|
||||
createBaseRequest(),
|
||||
mockEnvVars
|
||||
)
|
||||
|
||||
expect(fetchInitialDigestAuthInfo).toHaveBeenCalledWith(
|
||||
"https://api.example.com/data",
|
||||
"GET"
|
||||
)
|
||||
|
||||
expect(headers[0].value).toBe(mockDigestHeader)
|
||||
})
|
||||
|
||||
test("handles empty credentials", async () => {
|
||||
const mockDigestHeader =
|
||||
'Digest username="", realm="realm", nonce="nonce", uri="/api/data", algorithm="MD5", response="response", qop=auth, nc=00000001, cnonce="cnonce"'
|
||||
|
||||
vi.mocked(generateDigestAuthHeader).mockResolvedValue(mockDigestHeader)
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "digest" } = {
|
||||
authActive: true,
|
||||
authType: "digest",
|
||||
username: "",
|
||||
password: "",
|
||||
realm: "realm",
|
||||
nonce: "nonce",
|
||||
algorithm: "MD5",
|
||||
qop: "auth",
|
||||
nc: "00000001",
|
||||
cnonce: "",
|
||||
opaque: "",
|
||||
disableRetry: false,
|
||||
}
|
||||
|
||||
const headers = await generateDigestAuthHeaders(
|
||||
auth,
|
||||
createBaseRequest(),
|
||||
mockEnvVars
|
||||
)
|
||||
|
||||
expect(headers[0].value).toBe(mockDigestHeader)
|
||||
})
|
||||
|
||||
test("handles digest auth generation failure", async () => {
|
||||
vi.mocked(generateDigestAuthHeader).mockResolvedValue("")
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "digest" } = {
|
||||
authActive: true,
|
||||
authType: "digest",
|
||||
username: "user",
|
||||
password: "pass",
|
||||
realm: "realm",
|
||||
nonce: "nonce",
|
||||
algorithm: "MD5",
|
||||
qop: "auth",
|
||||
nc: "00000001",
|
||||
cnonce: "",
|
||||
opaque: "",
|
||||
disableRetry: false,
|
||||
}
|
||||
|
||||
const headers = await generateDigestAuthHeaders(
|
||||
auth,
|
||||
createBaseRequest(),
|
||||
mockEnvVars
|
||||
)
|
||||
|
||||
expect(headers).toHaveLength(1)
|
||||
expect(headers[0].value).toBe("")
|
||||
})
|
||||
|
||||
test("handles different HTTP methods", async () => {
|
||||
const methods = ["GET", "POST", "PUT", "DELETE", "PATCH"]
|
||||
|
||||
for (const method of methods) {
|
||||
const mockDigestHeader = `Digest username="user", realm="realm", nonce="nonce", uri="/api/data", algorithm="MD5", response="response-${method}", qop=auth, nc=00000001, cnonce="cnonce"`
|
||||
|
||||
vi.mocked(generateDigestAuthHeader).mockResolvedValue(mockDigestHeader)
|
||||
|
||||
const requestWithMethod = createBaseRequest({
|
||||
method,
|
||||
})
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "digest" } = {
|
||||
authActive: true,
|
||||
authType: "digest",
|
||||
username: "user",
|
||||
password: "pass",
|
||||
realm: "realm",
|
||||
nonce: "nonce",
|
||||
algorithm: "MD5",
|
||||
qop: "auth",
|
||||
nc: "00000001",
|
||||
cnonce: "",
|
||||
opaque: "",
|
||||
disableRetry: false,
|
||||
}
|
||||
|
||||
const headers = await generateDigestAuthHeaders(
|
||||
auth,
|
||||
requestWithMethod,
|
||||
mockEnvVars
|
||||
)
|
||||
|
||||
expect(generateDigestAuthHeader).toHaveBeenCalledWith({
|
||||
username: "user",
|
||||
password: "pass",
|
||||
realm: "realm",
|
||||
nonce: "nonce",
|
||||
endpoint: "https://api.example.com/data",
|
||||
method,
|
||||
algorithm: "MD5",
|
||||
qop: "auth",
|
||||
opaque: "",
|
||||
reqBody: "",
|
||||
})
|
||||
|
||||
expect(headers[0].value).toBe(mockDigestHeader)
|
||||
vi.clearAllMocks()
|
||||
}
|
||||
})
|
||||
|
||||
test("handles disable retry flag", async () => {
|
||||
const mockDigestHeader =
|
||||
'Digest username="user", realm="realm", nonce="nonce", uri="/api/data", algorithm="MD5", response="response", qop=auth, nc=00000001, cnonce="cnonce"'
|
||||
|
||||
vi.mocked(generateDigestAuthHeader).mockResolvedValue(mockDigestHeader)
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "digest" } = {
|
||||
authActive: true,
|
||||
authType: "digest",
|
||||
username: "user",
|
||||
password: "pass",
|
||||
realm: "realm",
|
||||
nonce: "nonce",
|
||||
algorithm: "MD5",
|
||||
qop: "auth",
|
||||
nc: "00000001",
|
||||
cnonce: "",
|
||||
opaque: "",
|
||||
disableRetry: true,
|
||||
}
|
||||
|
||||
const headers = await generateDigestAuthHeaders(
|
||||
auth,
|
||||
createBaseRequest(),
|
||||
mockEnvVars
|
||||
)
|
||||
|
||||
expect(headers[0].value).toBe(mockDigestHeader)
|
||||
// The disableRetry flag would be used in the actual digest auth flow
|
||||
})
|
||||
|
||||
test("handles complex URI paths with query parameters", async () => {
|
||||
const mockDigestHeader =
|
||||
'Digest username="user", realm="realm", nonce="nonce", uri="/api/data?param=value&other=test", algorithm="MD5", response="response", qop=auth, nc=00000001, cnonce="cnonce"'
|
||||
|
||||
vi.mocked(generateDigestAuthHeader).mockResolvedValue(mockDigestHeader)
|
||||
|
||||
const requestWithQueryParams = createBaseRequest({
|
||||
endpoint: "https://api.example.com/api/data?param=value&other=test",
|
||||
})
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "digest" } = {
|
||||
authActive: true,
|
||||
authType: "digest",
|
||||
username: "user",
|
||||
password: "pass",
|
||||
realm: "realm",
|
||||
nonce: "nonce",
|
||||
algorithm: "MD5",
|
||||
qop: "auth",
|
||||
nc: "00000001",
|
||||
cnonce: "",
|
||||
opaque: "",
|
||||
disableRetry: false,
|
||||
}
|
||||
|
||||
const headers = await generateDigestAuthHeaders(
|
||||
auth,
|
||||
requestWithQueryParams,
|
||||
mockEnvVars
|
||||
)
|
||||
|
||||
expect(generateDigestAuthHeader).toHaveBeenCalledWith({
|
||||
username: "user",
|
||||
password: "pass",
|
||||
realm: "realm",
|
||||
nonce: "nonce",
|
||||
endpoint: "https://api.example.com/api/data?param=value&other=test",
|
||||
method: "GET",
|
||||
algorithm: "MD5",
|
||||
qop: "auth",
|
||||
opaque: "",
|
||||
reqBody: "",
|
||||
})
|
||||
|
||||
expect(headers[0].value).toBe(mockDigestHeader)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
import { describe, test, expect, vi, beforeEach } from "vitest"
|
||||
import { generateHawkAuthHeaders } from "../hawk"
|
||||
import { createBaseRequest, mockEnvVars } from "./test-utils"
|
||||
import { HoppRESTAuth } from "@hoppscotch/data"
|
||||
|
||||
// Mock the calculateHawkHeader function
|
||||
vi.mock("@hoppscotch/data", async () => {
|
||||
const actual = await vi.importActual("@hoppscotch/data")
|
||||
return {
|
||||
...actual,
|
||||
calculateHawkHeader: vi.fn(),
|
||||
}
|
||||
})
|
||||
|
||||
// Mock the getFinalBodyFromRequest function
|
||||
vi.mock("~/helpers/utils/EffectiveURL", () => ({
|
||||
getFinalBodyFromRequest: vi.fn(),
|
||||
}))
|
||||
|
||||
const { calculateHawkHeader } = await import("@hoppscotch/data")
|
||||
const { getFinalBodyFromRequest } = await import("~/helpers/utils/EffectiveURL")
|
||||
|
||||
describe("Hawk Auth", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe("generateHawkAuthHeaders", () => {
|
||||
test("generates hawk authorization header", async () => {
|
||||
vi.mocked(calculateHawkHeader).mockResolvedValue(
|
||||
'Hawk id="test-hawk-id", ts="1234567890", nonce="abcdef", mac="xyz123"'
|
||||
)
|
||||
vi.mocked(getFinalBodyFromRequest).mockReturnValue('{"test": "data"}')
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "hawk" } = {
|
||||
authActive: true,
|
||||
authType: "hawk",
|
||||
authId: "<<HAWK_ID>>",
|
||||
authKey: "<<HAWK_KEY>>",
|
||||
algorithm: "sha256",
|
||||
includePayloadHash: true,
|
||||
}
|
||||
|
||||
const headers = await generateHawkAuthHeaders(
|
||||
auth,
|
||||
createBaseRequest(),
|
||||
mockEnvVars
|
||||
)
|
||||
|
||||
expect(headers).toHaveLength(1)
|
||||
expect(headers[0]).toEqual({
|
||||
active: true,
|
||||
key: "Authorization",
|
||||
value:
|
||||
'Hawk id="test-hawk-id", ts="1234567890", nonce="abcdef", mac="xyz123"',
|
||||
description: "",
|
||||
})
|
||||
})
|
||||
|
||||
test("returns empty array for non-hawk auth type", async () => {
|
||||
const auth: HoppRESTAuth & { authType: "basic" } = {
|
||||
authActive: true,
|
||||
authType: "basic",
|
||||
username: "user",
|
||||
password: "pass",
|
||||
}
|
||||
|
||||
const headers = await generateHawkAuthHeaders(
|
||||
auth,
|
||||
createBaseRequest(),
|
||||
mockEnvVars
|
||||
)
|
||||
|
||||
expect(headers).toHaveLength(0)
|
||||
})
|
||||
|
||||
test("handles template strings in auth parameters", async () => {
|
||||
vi.mocked(calculateHawkHeader).mockResolvedValue(
|
||||
'Hawk id="test-hawk-id", mac="xyz123"'
|
||||
)
|
||||
vi.mocked(getFinalBodyFromRequest).mockReturnValue("")
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "hawk" } = {
|
||||
authActive: true,
|
||||
authType: "hawk",
|
||||
authId: "<<HAWK_ID>>",
|
||||
authKey: "<<HAWK_KEY>>",
|
||||
algorithm: "sha1",
|
||||
includePayloadHash: false,
|
||||
}
|
||||
|
||||
await generateHawkAuthHeaders(auth, createBaseRequest(), mockEnvVars)
|
||||
|
||||
expect(calculateHawkHeader).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
id: "test-hawk-id",
|
||||
key: "test-hawk-key",
|
||||
algorithm: "sha1",
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
test("handles optional hawk parameters", async () => {
|
||||
vi.mocked(calculateHawkHeader).mockResolvedValue(
|
||||
'Hawk id="test-hawk-id", mac="xyz123"'
|
||||
)
|
||||
vi.mocked(getFinalBodyFromRequest).mockReturnValue("")
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "hawk" } = {
|
||||
authActive: true,
|
||||
authType: "hawk",
|
||||
authId: "<<HAWK_ID>>",
|
||||
authKey: "<<HAWK_KEY>>",
|
||||
algorithm: "sha256",
|
||||
nonce: "custom-nonce",
|
||||
ext: "custom-ext",
|
||||
app: "custom-app",
|
||||
dlg: "custom-dlg",
|
||||
timestamp: "1234567890",
|
||||
includePayloadHash: false,
|
||||
}
|
||||
|
||||
await generateHawkAuthHeaders(auth, createBaseRequest(), mockEnvVars)
|
||||
|
||||
expect(calculateHawkHeader).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
nonce: "custom-nonce",
|
||||
ext: "custom-ext",
|
||||
app: "custom-app",
|
||||
dlg: "custom-dlg",
|
||||
timestamp: 1234567890,
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,334 @@
|
|||
import { describe, test, expect, vi, beforeEach } from "vitest"
|
||||
import { generateJwtAuthHeaders } from "../jwt"
|
||||
import { createBaseRequest, mockEnvVars } from "./test-utils"
|
||||
import { HoppRESTAuth } from "@hoppscotch/data"
|
||||
|
||||
// Mock the jwt helper
|
||||
vi.mock("@hoppscotch/data", async () => {
|
||||
const actual = await vi.importActual("@hoppscotch/data")
|
||||
return {
|
||||
...actual,
|
||||
generateJWTToken: vi.fn(),
|
||||
}
|
||||
})
|
||||
|
||||
import { generateJWTToken } from "@hoppscotch/data"
|
||||
|
||||
describe("JWT Auth", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe("generateJwtAuthHeaders", () => {
|
||||
test("generates JWT auth header with basic configuration", async () => {
|
||||
const mockToken =
|
||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
|
||||
|
||||
vi.mocked(generateJWTToken).mockResolvedValue(mockToken)
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "jwt" } = {
|
||||
authActive: true,
|
||||
authType: "jwt",
|
||||
secret: "my-secret-key",
|
||||
privateKey: "",
|
||||
algorithm: "HS256",
|
||||
payload: '{"sub": "1234567890", "name": "John Doe"}',
|
||||
addTo: "HEADERS",
|
||||
isSecretBase64Encoded: false,
|
||||
headerPrefix: "Bearer ",
|
||||
paramName: "token",
|
||||
jwtHeaders: "{}",
|
||||
}
|
||||
|
||||
const headers = await generateJwtAuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(generateJWTToken).toHaveBeenCalledWith({
|
||||
secret: "my-secret-key",
|
||||
algorithm: "HS256",
|
||||
payload: '{"sub": "1234567890", "name": "John Doe"}',
|
||||
isSecretBase64Encoded: false,
|
||||
privateKey: "",
|
||||
jwtHeaders: "{}",
|
||||
})
|
||||
|
||||
expect(headers).toHaveLength(1)
|
||||
expect(headers[0]).toEqual({
|
||||
active: true,
|
||||
key: "Authorization",
|
||||
value:
|
||||
"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
||||
description: "",
|
||||
})
|
||||
})
|
||||
|
||||
test("adds JWT token to query params when addTo is QUERY_PARAMS", async () => {
|
||||
const mockToken = "jwt.token.here"
|
||||
vi.mocked(generateJWTToken).mockResolvedValue(mockToken)
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "jwt" } = {
|
||||
authActive: true,
|
||||
authType: "jwt",
|
||||
secret: "secret",
|
||||
privateKey: "",
|
||||
algorithm: "HS256",
|
||||
payload: "{}",
|
||||
addTo: "QUERY_PARAMS",
|
||||
isSecretBase64Encoded: false,
|
||||
headerPrefix: "Bearer ",
|
||||
paramName: "access_token",
|
||||
jwtHeaders: "{}",
|
||||
}
|
||||
|
||||
const headers = await generateJwtAuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(headers).toHaveLength(0)
|
||||
// Note: Query params would be handled differently in the actual implementation
|
||||
})
|
||||
|
||||
test("handles template variables in secret", async () => {
|
||||
const mockToken = "generated.jwt.token"
|
||||
vi.mocked(generateJWTToken).mockResolvedValue(mockToken)
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "jwt" } = {
|
||||
authActive: true,
|
||||
authType: "jwt",
|
||||
secret: "<<JWT_SECRET>>",
|
||||
privateKey: "",
|
||||
algorithm: "HS256",
|
||||
payload: "<<JWT_PAYLOAD>>",
|
||||
addTo: "HEADERS",
|
||||
isSecretBase64Encoded: false,
|
||||
headerPrefix: "Bearer ",
|
||||
paramName: "token",
|
||||
jwtHeaders: "{}",
|
||||
}
|
||||
|
||||
const headers = await generateJwtAuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(generateJWTToken).toHaveBeenCalledWith({
|
||||
secret: "my-secret-key",
|
||||
algorithm: "HS256",
|
||||
payload: '{"sub": "1234567890", "name": "John Doe"}',
|
||||
isSecretBase64Encoded: false,
|
||||
privateKey: "",
|
||||
jwtHeaders: "{}",
|
||||
})
|
||||
|
||||
expect(headers[0].value).toBe("Bearer generated.jwt.token")
|
||||
})
|
||||
|
||||
test("handles RSA algorithm with private key", async () => {
|
||||
const mockToken = "rsa.signed.token"
|
||||
vi.mocked(generateJWTToken).mockResolvedValue(mockToken)
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "jwt" } = {
|
||||
authActive: true,
|
||||
authType: "jwt",
|
||||
secret: "",
|
||||
privateKey:
|
||||
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4...",
|
||||
algorithm: "RS256",
|
||||
payload: '{"iss": "test"}',
|
||||
addTo: "HEADERS",
|
||||
isSecretBase64Encoded: false,
|
||||
headerPrefix: "JWT ",
|
||||
paramName: "token",
|
||||
jwtHeaders: '{"typ": "JWT"}',
|
||||
}
|
||||
|
||||
const headers = await generateJwtAuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(generateJWTToken).toHaveBeenCalledWith({
|
||||
secret: "",
|
||||
algorithm: "RS256",
|
||||
payload: '{"iss": "test"}',
|
||||
isSecretBase64Encoded: false,
|
||||
privateKey:
|
||||
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4...",
|
||||
jwtHeaders: '{"typ": "JWT"}',
|
||||
})
|
||||
|
||||
expect(headers[0].value).toBe("JWT rsa.signed.token")
|
||||
})
|
||||
|
||||
test("handles base64 encoded secret", async () => {
|
||||
const mockToken = "base64.encoded.token"
|
||||
vi.mocked(generateJWTToken).mockResolvedValue(mockToken)
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "jwt" } = {
|
||||
authActive: true,
|
||||
authType: "jwt",
|
||||
secret: "bXktc2VjcmV0LWtleQ==", // base64 for "my-secret-key"
|
||||
privateKey: "",
|
||||
algorithm: "HS512",
|
||||
payload: "{}",
|
||||
addTo: "HEADERS",
|
||||
isSecretBase64Encoded: true,
|
||||
headerPrefix: "Token ",
|
||||
paramName: "token",
|
||||
jwtHeaders: "{}",
|
||||
}
|
||||
|
||||
const headers = await generateJwtAuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(generateJWTToken).toHaveBeenCalledWith({
|
||||
secret: "bXktc2VjcmV0LWtleQ==",
|
||||
algorithm: "HS512",
|
||||
payload: "{}",
|
||||
isSecretBase64Encoded: true,
|
||||
privateKey: "",
|
||||
jwtHeaders: "{}",
|
||||
})
|
||||
|
||||
expect(headers[0].value).toBe("Token base64.encoded.token")
|
||||
})
|
||||
|
||||
test("handles custom header prefix", async () => {
|
||||
const mockToken = "custom.prefix.token"
|
||||
vi.mocked(generateJWTToken).mockResolvedValue(mockToken)
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "jwt" } = {
|
||||
authActive: true,
|
||||
authType: "jwt",
|
||||
secret: "secret",
|
||||
privateKey: "",
|
||||
algorithm: "HS256",
|
||||
payload: "{}",
|
||||
addTo: "HEADERS",
|
||||
isSecretBase64Encoded: false,
|
||||
headerPrefix: "Custom-Auth ",
|
||||
paramName: "token",
|
||||
jwtHeaders: "{}",
|
||||
}
|
||||
|
||||
const headers = await generateJwtAuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(headers[0].value).toBe("Custom-Auth custom.prefix.token")
|
||||
})
|
||||
|
||||
test("handles empty header prefix", async () => {
|
||||
const mockToken = "no.prefix.token"
|
||||
vi.mocked(generateJWTToken).mockResolvedValue(mockToken)
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "jwt" } = {
|
||||
authActive: true,
|
||||
authType: "jwt",
|
||||
secret: "secret",
|
||||
privateKey: "",
|
||||
algorithm: "HS256",
|
||||
payload: "{}",
|
||||
addTo: "HEADERS",
|
||||
isSecretBase64Encoded: false,
|
||||
headerPrefix: "",
|
||||
paramName: "token",
|
||||
jwtHeaders: "{}",
|
||||
}
|
||||
|
||||
const headers = await generateJwtAuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(headers[0].value).toBe("no.prefix.token")
|
||||
})
|
||||
|
||||
test("handles JWT generation failure", async () => {
|
||||
vi.mocked(generateJWTToken).mockResolvedValue(null)
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "jwt" } = {
|
||||
authActive: true,
|
||||
authType: "jwt",
|
||||
secret: "",
|
||||
privateKey: "",
|
||||
algorithm: "HS256",
|
||||
payload: "{}",
|
||||
addTo: "HEADERS",
|
||||
isSecretBase64Encoded: false,
|
||||
headerPrefix: "Bearer ",
|
||||
paramName: "token",
|
||||
jwtHeaders: "{}",
|
||||
}
|
||||
|
||||
const headers = await generateJwtAuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(headers).toHaveLength(0)
|
||||
})
|
||||
|
||||
test("handles different JWT algorithms", async () => {
|
||||
const algorithms = ["HS384", "RS384", "PS256", "ES256"] as const
|
||||
|
||||
for (const algorithm of algorithms) {
|
||||
const mockToken = `${algorithm.toLowerCase()}.token`
|
||||
vi.mocked(generateJWTToken).mockResolvedValue(mockToken)
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "jwt" } = {
|
||||
authActive: true,
|
||||
authType: "jwt",
|
||||
secret: "secret",
|
||||
privateKey: algorithm.startsWith("HS") ? "" : "private-key",
|
||||
algorithm,
|
||||
payload: "{}",
|
||||
addTo: "HEADERS",
|
||||
isSecretBase64Encoded: false,
|
||||
headerPrefix: "Bearer ",
|
||||
paramName: "token",
|
||||
jwtHeaders: "{}",
|
||||
}
|
||||
|
||||
const headers = await generateJwtAuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(generateJWTToken).toHaveBeenCalledWith({
|
||||
secret: "secret",
|
||||
algorithm,
|
||||
payload: "{}",
|
||||
isSecretBase64Encoded: false,
|
||||
privateKey: algorithm.startsWith("HS") ? "" : "private-key",
|
||||
jwtHeaders: "{}",
|
||||
})
|
||||
|
||||
expect(headers[0].value).toBe(`Bearer ${mockToken}`)
|
||||
vi.clearAllMocks()
|
||||
}
|
||||
})
|
||||
|
||||
test("handles complex payload with claims", async () => {
|
||||
const mockToken = "complex.payload.token"
|
||||
vi.mocked(generateJWTToken).mockResolvedValue(mockToken)
|
||||
|
||||
const complexPayload = JSON.stringify({
|
||||
iss: "https://example.com",
|
||||
sub: "user123",
|
||||
aud: "api.example.com",
|
||||
exp: Math.floor(Date.now() / 1000) + 3600,
|
||||
iat: Math.floor(Date.now() / 1000),
|
||||
nbf: Math.floor(Date.now() / 1000),
|
||||
jti: "unique-id",
|
||||
custom_claim: "custom_value",
|
||||
})
|
||||
|
||||
const auth: HoppRESTAuth & { authType: "jwt" } = {
|
||||
authActive: true,
|
||||
authType: "jwt",
|
||||
secret: "secret",
|
||||
privateKey: "",
|
||||
algorithm: "HS256",
|
||||
payload: complexPayload,
|
||||
addTo: "HEADERS",
|
||||
isSecretBase64Encoded: false,
|
||||
headerPrefix: "Bearer ",
|
||||
paramName: "token",
|
||||
jwtHeaders: '{"alg": "HS256", "typ": "JWT", "kid": "key-id"}',
|
||||
}
|
||||
|
||||
const headers = await generateJwtAuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(generateJWTToken).toHaveBeenCalledWith({
|
||||
secret: "secret",
|
||||
algorithm: "HS256",
|
||||
payload: complexPayload,
|
||||
isSecretBase64Encoded: false,
|
||||
privateKey: "",
|
||||
jwtHeaders: '{"alg": "HS256", "typ": "JWT", "kid": "key-id"}',
|
||||
})
|
||||
|
||||
expect(headers[0].value).toBe("Bearer complex.payload.token")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
import { describe, test, expect } from "vitest"
|
||||
import { generateOAuth2AuthHeaders } from "../oauth2"
|
||||
import { createBaseRequest, mockEnvVars } from "./test-utils"
|
||||
import { HoppRESTAuth } from "@hoppscotch/data"
|
||||
|
||||
describe("OAuth2 Auth", () => {
|
||||
describe("generateOAuth2AuthHeaders", () => {
|
||||
test("generates OAuth2 auth header for Authorization Code grant type", async () => {
|
||||
const auth: HoppRESTAuth & { authType: "oauth-2" } = {
|
||||
authActive: true,
|
||||
authType: "oauth-2",
|
||||
grantTypeInfo: {
|
||||
grantType: "AUTHORIZATION_CODE",
|
||||
authEndpoint: "https://auth.example.com/oauth/authorize",
|
||||
tokenEndpoint: "https://auth.example.com/oauth/token",
|
||||
clientID: "my-client-id",
|
||||
clientSecret: "my-client-secret",
|
||||
scopes: "read write",
|
||||
token: "oauth2_access_token_123",
|
||||
isPKCE: false,
|
||||
refreshToken: "refresh_token_456",
|
||||
authRequestParams: [],
|
||||
tokenRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
},
|
||||
addTo: "HEADERS",
|
||||
}
|
||||
|
||||
const headers = await generateOAuth2AuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(headers).toHaveLength(1)
|
||||
expect(headers[0]).toEqual({
|
||||
active: true,
|
||||
key: "Authorization",
|
||||
value: "Bearer oauth2_access_token_123",
|
||||
description: "",
|
||||
})
|
||||
})
|
||||
|
||||
test("adds OAuth2 token to query params when addTo is QUERY_PARAMS", async () => {
|
||||
const auth: HoppRESTAuth & { authType: "oauth-2" } = {
|
||||
authActive: true,
|
||||
authType: "oauth-2",
|
||||
grantTypeInfo: {
|
||||
grantType: "AUTHORIZATION_CODE",
|
||||
authEndpoint: "https://auth.example.com/oauth/authorize",
|
||||
tokenEndpoint: "https://auth.example.com/oauth/token",
|
||||
clientID: "client-id",
|
||||
clientSecret: "client-secret",
|
||||
scopes: "read",
|
||||
token: "query_param_token",
|
||||
isPKCE: false,
|
||||
authRequestParams: [],
|
||||
tokenRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
},
|
||||
addTo: "QUERY_PARAMS",
|
||||
}
|
||||
|
||||
const headers = await generateOAuth2AuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(headers).toHaveLength(0)
|
||||
// Note: Query params would be handled differently in the actual implementation
|
||||
})
|
||||
|
||||
test("handles Client Credentials grant type", async () => {
|
||||
const auth: HoppRESTAuth & { authType: "oauth-2" } = {
|
||||
authActive: true,
|
||||
authType: "oauth-2",
|
||||
grantTypeInfo: {
|
||||
grantType: "CLIENT_CREDENTIALS",
|
||||
authEndpoint: "https://auth.example.com/oauth/token",
|
||||
clientID: "client-credentials-id",
|
||||
clientSecret: "client-credentials-secret",
|
||||
scopes: "api:read api:write",
|
||||
token: "client_credentials_token",
|
||||
clientAuthentication: "AS_BASIC_AUTH_HEADERS",
|
||||
tokenRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
},
|
||||
addTo: "HEADERS",
|
||||
}
|
||||
|
||||
const headers = await generateOAuth2AuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(headers).toHaveLength(1)
|
||||
expect(headers[0]).toEqual({
|
||||
active: true,
|
||||
key: "Authorization",
|
||||
value: "Bearer client_credentials_token",
|
||||
description: "",
|
||||
})
|
||||
})
|
||||
|
||||
test("handles Password grant type", async () => {
|
||||
const auth: HoppRESTAuth & { authType: "oauth-2" } = {
|
||||
authActive: true,
|
||||
authType: "oauth-2",
|
||||
grantTypeInfo: {
|
||||
grantType: "PASSWORD",
|
||||
authEndpoint: "https://auth.example.com/oauth/token",
|
||||
clientID: "password-client-id",
|
||||
clientSecret: "password-client-secret",
|
||||
scopes: "user:profile",
|
||||
username: "testuser",
|
||||
password: "testpass",
|
||||
token: "password_grant_token",
|
||||
tokenRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
},
|
||||
addTo: "HEADERS",
|
||||
}
|
||||
|
||||
const headers = await generateOAuth2AuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(headers).toHaveLength(1)
|
||||
expect(headers[0]).toEqual({
|
||||
active: true,
|
||||
key: "Authorization",
|
||||
value: "Bearer password_grant_token",
|
||||
description: "",
|
||||
})
|
||||
})
|
||||
|
||||
test("handles Implicit grant type", async () => {
|
||||
const auth: HoppRESTAuth & { authType: "oauth-2" } = {
|
||||
authActive: true,
|
||||
authType: "oauth-2",
|
||||
grantTypeInfo: {
|
||||
grantType: "IMPLICIT",
|
||||
authEndpoint: "https://auth.example.com/oauth/authorize",
|
||||
clientID: "implicit-client-id",
|
||||
scopes: "read",
|
||||
token: "implicit_token_123",
|
||||
authRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
},
|
||||
addTo: "HEADERS",
|
||||
}
|
||||
|
||||
const headers = await generateOAuth2AuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(headers).toHaveLength(1)
|
||||
expect(headers[0]).toEqual({
|
||||
active: true,
|
||||
key: "Authorization",
|
||||
value: "Bearer implicit_token_123",
|
||||
description: "",
|
||||
})
|
||||
})
|
||||
|
||||
test("handles template variables in token", async () => {
|
||||
const auth: HoppRESTAuth & { authType: "oauth-2" } = {
|
||||
authActive: true,
|
||||
authType: "oauth-2",
|
||||
grantTypeInfo: {
|
||||
grantType: "AUTHORIZATION_CODE",
|
||||
authEndpoint: "https://auth.example.com/oauth/authorize",
|
||||
tokenEndpoint: "https://auth.example.com/oauth/token",
|
||||
clientID: "<<CLIENT_ID>>",
|
||||
clientSecret: "<<CLIENT_SECRET>>",
|
||||
scopes: "read write",
|
||||
token: "<<OAUTH_TOKEN>>",
|
||||
isPKCE: false,
|
||||
authRequestParams: [],
|
||||
tokenRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
},
|
||||
addTo: "HEADERS",
|
||||
}
|
||||
|
||||
const headers = await generateOAuth2AuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(headers[0].value).toBe("Bearer oauth2_access_token_123")
|
||||
})
|
||||
|
||||
test("handles empty token", async () => {
|
||||
const auth: HoppRESTAuth & { authType: "oauth-2" } = {
|
||||
authActive: true,
|
||||
authType: "oauth-2",
|
||||
grantTypeInfo: {
|
||||
grantType: "AUTHORIZATION_CODE",
|
||||
authEndpoint: "https://auth.example.com/oauth/authorize",
|
||||
tokenEndpoint: "https://auth.example.com/oauth/token",
|
||||
clientID: "client-id",
|
||||
clientSecret: "client-secret",
|
||||
scopes: "read",
|
||||
token: "",
|
||||
isPKCE: false,
|
||||
authRequestParams: [],
|
||||
tokenRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
},
|
||||
addTo: "HEADERS",
|
||||
}
|
||||
|
||||
const headers = await generateOAuth2AuthHeaders(auth, mockEnvVars)
|
||||
|
||||
expect(headers[0].value).toBe("Bearer ")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
import { Environment, makeRESTRequest } from "@hoppscotch/data"
|
||||
|
||||
// Helper function to create base request
|
||||
export const createBaseRequest = (
|
||||
overrides: Partial<Parameters<typeof makeRESTRequest>[0]> = {}
|
||||
) => {
|
||||
const baseRequest: Parameters<typeof makeRESTRequest>[0] = {
|
||||
method: "GET",
|
||||
endpoint: "https://api.example.com/data",
|
||||
name: "Test Request",
|
||||
params: [],
|
||||
headers: [],
|
||||
preRequestScript: "",
|
||||
testScript: "",
|
||||
auth: {
|
||||
authType: "inherit",
|
||||
authActive: true,
|
||||
},
|
||||
body: {
|
||||
contentType: null,
|
||||
body: null,
|
||||
},
|
||||
requestVariables: [],
|
||||
responses: {},
|
||||
}
|
||||
|
||||
return makeRESTRequest({ ...baseRequest, ...overrides })
|
||||
}
|
||||
|
||||
export const mockEnvVars: Environment["variables"] = [
|
||||
{
|
||||
key: "API_KEY",
|
||||
secret: false,
|
||||
initialValue: "test-key-123",
|
||||
currentValue: "test-key-123",
|
||||
},
|
||||
{
|
||||
key: "API_VALUE",
|
||||
secret: true,
|
||||
initialValue: "secret-value",
|
||||
currentValue: "secret-value",
|
||||
},
|
||||
{
|
||||
key: "ACCESS_TOKEN",
|
||||
secret: true,
|
||||
initialValue: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9",
|
||||
currentValue: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9",
|
||||
},
|
||||
{
|
||||
key: "USERNAME",
|
||||
secret: false,
|
||||
initialValue: "testuser",
|
||||
currentValue: "testuser",
|
||||
},
|
||||
{
|
||||
key: "PASSWORD",
|
||||
secret: true,
|
||||
initialValue: "testpass",
|
||||
currentValue: "testpass",
|
||||
},
|
||||
{
|
||||
key: "OAUTH_TOKEN",
|
||||
secret: true,
|
||||
initialValue: "oauth2_access_token_123",
|
||||
currentValue: "oauth2_access_token_123",
|
||||
},
|
||||
{
|
||||
key: "JWT_SECRET",
|
||||
secret: true,
|
||||
initialValue: "my-secret-key",
|
||||
currentValue: "my-secret-key",
|
||||
},
|
||||
{
|
||||
key: "JWT_PAYLOAD",
|
||||
secret: false,
|
||||
initialValue: '{"sub": "1234567890", "name": "John Doe"}',
|
||||
currentValue: '{"sub": "1234567890", "name": "John Doe"}',
|
||||
},
|
||||
{
|
||||
key: "AWS_ACCESS_KEY",
|
||||
secret: true,
|
||||
initialValue: "AKIAIOSFODNN7EXAMPLE",
|
||||
currentValue: "AKIAIOSFODNN7EXAMPLE",
|
||||
},
|
||||
{
|
||||
key: "AWS_SECRET_KEY",
|
||||
secret: true,
|
||||
initialValue: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
||||
currentValue: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
||||
},
|
||||
{
|
||||
key: "AWS_REGION",
|
||||
secret: false,
|
||||
initialValue: "us-east-1",
|
||||
currentValue: "us-east-1",
|
||||
},
|
||||
{
|
||||
key: "HAWK_ID",
|
||||
secret: false,
|
||||
initialValue: "test-hawk-id",
|
||||
currentValue: "test-hawk-id",
|
||||
},
|
||||
{
|
||||
key: "HAWK_KEY",
|
||||
secret: true,
|
||||
initialValue: "test-hawk-key",
|
||||
currentValue: "test-hawk-key",
|
||||
},
|
||||
{
|
||||
key: "DIGEST_USER",
|
||||
secret: false,
|
||||
initialValue: "testuser",
|
||||
currentValue: "testuser",
|
||||
},
|
||||
{
|
||||
key: "DIGEST_PASS",
|
||||
secret: true,
|
||||
initialValue: "testpass",
|
||||
currentValue: "testpass",
|
||||
},
|
||||
]
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import {
|
||||
parseTemplateString,
|
||||
HoppRESTAuth,
|
||||
HoppRESTRequest,
|
||||
Environment,
|
||||
HoppRESTHeader,
|
||||
HoppRESTParam,
|
||||
|
|
@ -9,7 +8,6 @@ import {
|
|||
|
||||
export async function generateApiKeyAuthHeaders(
|
||||
auth: HoppRESTAuth & { authType: "api-key" },
|
||||
request: HoppRESTRequest,
|
||||
envVars: Environment["variables"],
|
||||
showKeyIfSecret = false
|
||||
): Promise<HoppRESTHeader[]> {
|
||||
|
|
@ -32,7 +30,6 @@ export async function generateApiKeyAuthHeaders(
|
|||
|
||||
export async function generateApiKeyAuthParams(
|
||||
auth: HoppRESTAuth & { authType: "api-key" },
|
||||
request: HoppRESTRequest,
|
||||
envVars: Environment["variables"],
|
||||
showKeyIfSecret = false
|
||||
): Promise<HoppRESTParam[]> {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
import {
|
||||
parseTemplateString,
|
||||
HoppRESTAuth,
|
||||
HoppRESTRequest,
|
||||
Environment,
|
||||
HoppRESTHeader,
|
||||
} from "@hoppscotch/data"
|
||||
|
||||
export async function generateBasicAuthHeaders(
|
||||
auth: HoppRESTAuth & { authType: "basic" },
|
||||
request: HoppRESTRequest,
|
||||
envVars: Environment["variables"],
|
||||
showKeyIfSecret = false
|
||||
): Promise<HoppRESTHeader[]> {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
import {
|
||||
parseTemplateString,
|
||||
HoppRESTAuth,
|
||||
HoppRESTRequest,
|
||||
Environment,
|
||||
HoppRESTHeader,
|
||||
} from "@hoppscotch/data"
|
||||
|
||||
export async function generateBearerAuthHeaders(
|
||||
auth: HoppRESTAuth & { authType: "bearer" },
|
||||
request: HoppRESTRequest,
|
||||
envVars: Environment["variables"],
|
||||
showKeyIfSecret = false
|
||||
): Promise<HoppRESTHeader[]> {
|
||||
|
|
|
|||
|
|
@ -4,13 +4,11 @@ import {
|
|||
HoppRESTAuth,
|
||||
HoppRESTHeader,
|
||||
HoppRESTParam,
|
||||
HoppRESTRequest,
|
||||
parseTemplateString,
|
||||
} from "@hoppscotch/data"
|
||||
|
||||
export async function generateJwtAuthHeaders(
|
||||
auth: HoppRESTAuth & { authType: "jwt" },
|
||||
request: HoppRESTRequest,
|
||||
envVars: Environment["variables"],
|
||||
showKeyIfSecret = false
|
||||
): Promise<HoppRESTHeader[]> {
|
||||
|
|
@ -47,7 +45,6 @@ export async function generateJwtAuthHeaders(
|
|||
|
||||
export async function generateJwtAuthParams(
|
||||
auth: HoppRESTAuth & { authType: "jwt" },
|
||||
request: HoppRESTRequest,
|
||||
envVars: Environment["variables"]
|
||||
): Promise<HoppRESTParam[]> {
|
||||
if (auth.addTo !== "QUERY_PARAMS") return []
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import {
|
||||
parseTemplateString,
|
||||
HoppRESTAuth,
|
||||
HoppRESTRequest,
|
||||
Environment,
|
||||
HoppRESTHeader,
|
||||
HoppRESTParam,
|
||||
|
|
@ -9,7 +8,6 @@ import {
|
|||
|
||||
export async function generateOAuth2AuthHeaders(
|
||||
auth: HoppRESTAuth & { authType: "oauth-2" },
|
||||
request: HoppRESTRequest,
|
||||
envVars: Environment["variables"],
|
||||
showKeyIfSecret = false
|
||||
): Promise<HoppRESTHeader[]> {
|
||||
|
|
@ -34,7 +32,6 @@ export async function generateOAuth2AuthHeaders(
|
|||
|
||||
export async function generateOAuth2AuthParams(
|
||||
auth: HoppRESTAuth & { authType: "oauth-2" },
|
||||
request: HoppRESTRequest,
|
||||
envVars: Environment["variables"],
|
||||
showKeyIfSecret = false
|
||||
): Promise<HoppRESTParam[]> {
|
||||
|
|
|
|||
Loading…
Reference in a new issue