From 4f393d37a50cfe666ccc6e5b8450c85993675cec Mon Sep 17 00:00:00 2001 From: Anwarul Islam Date: Wed, 27 Aug 2025 10:54:21 +0600 Subject: [PATCH] test(common): add comprehensive unit tests for auth helpers (#5211) Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com> --- .../src/helpers/auth/auth-types.ts | 16 +- .../auth/types/__tests__/api-key.spec.ts | 93 +++ .../types/__tests__/aws-signature.spec.ts | 33 +- .../auth/types/__tests__/basic.spec.ts | 53 ++ .../auth/types/__tests__/bearer.spec.ts | 52 ++ .../auth/types/__tests__/digest.spec.ts | 557 ++++++++++++++++++ .../helpers/auth/types/__tests__/hawk.spec.ts | 136 +++++ .../helpers/auth/types/__tests__/jwt.spec.ts | 334 +++++++++++ .../auth/types/__tests__/oauth2.spec.ts | 202 +++++++ .../auth/types/__tests__/test-utils.ts | 121 ++++ .../src/helpers/auth/types/api-key.ts | 3 - .../src/helpers/auth/types/basic.ts | 2 - .../src/helpers/auth/types/bearer.ts | 2 - .../src/helpers/auth/types/jwt.ts | 3 - .../src/helpers/auth/types/oauth2.ts | 3 - 15 files changed, 1559 insertions(+), 51 deletions(-) create mode 100644 packages/hoppscotch-common/src/helpers/auth/types/__tests__/api-key.spec.ts create mode 100644 packages/hoppscotch-common/src/helpers/auth/types/__tests__/basic.spec.ts create mode 100644 packages/hoppscotch-common/src/helpers/auth/types/__tests__/bearer.spec.ts create mode 100644 packages/hoppscotch-common/src/helpers/auth/types/__tests__/digest.spec.ts create mode 100644 packages/hoppscotch-common/src/helpers/auth/types/__tests__/hawk.spec.ts create mode 100644 packages/hoppscotch-common/src/helpers/auth/types/__tests__/jwt.spec.ts create mode 100644 packages/hoppscotch-common/src/helpers/auth/types/__tests__/oauth2.spec.ts create mode 100644 packages/hoppscotch-common/src/helpers/auth/types/__tests__/test-utils.ts diff --git a/packages/hoppscotch-common/src/helpers/auth/auth-types.ts b/packages/hoppscotch-common/src/helpers/auth/auth-types.ts index 520e247d..d10dcc09 100644 --- a/packages/hoppscotch-common/src/helpers/auth/auth-types.ts +++ b/packages/hoppscotch-common/src/helpers/auth/auth-types.ts @@ -34,15 +34,15 @@ export async function generateAuthHeaders( ): Promise { 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 [] } diff --git a/packages/hoppscotch-common/src/helpers/auth/types/__tests__/api-key.spec.ts b/packages/hoppscotch-common/src/helpers/auth/types/__tests__/api-key.spec.ts new file mode 100644 index 00000000..d8f0ffb5 --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/auth/types/__tests__/api-key.spec.ts @@ -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: "<>", + } + + 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: "<>", + 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: "<>", + } + + 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) + }) + }) +}) diff --git a/packages/hoppscotch-common/src/helpers/auth/types/__tests__/aws-signature.spec.ts b/packages/hoppscotch-common/src/helpers/auth/types/__tests__/aws-signature.spec.ts index 54d94040..375fa0c3 100644 --- a/packages/hoppscotch-common/src/helpers/auth/types/__tests__/aws-signature.spec.ts +++ b/packages/hoppscotch-common/src/helpers/auth/types/__tests__/aws-signature.spec.ts @@ -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[0]> = {} - ) => { - const baseRequest: Parameters[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() diff --git a/packages/hoppscotch-common/src/helpers/auth/types/__tests__/basic.spec.ts b/packages/hoppscotch-common/src/helpers/auth/types/__tests__/basic.spec.ts new file mode 100644 index 00000000..b3e7ad1d --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/auth/types/__tests__/basic.spec.ts @@ -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: "<>", + 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(":")}`) + }) + }) +}) diff --git a/packages/hoppscotch-common/src/helpers/auth/types/__tests__/bearer.spec.ts b/packages/hoppscotch-common/src/helpers/auth/types/__tests__/bearer.spec.ts new file mode 100644 index 00000000..27ba0f71 --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/auth/types/__tests__/bearer.spec.ts @@ -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: "<>", + } + + 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 ") + }) + }) +}) diff --git a/packages/hoppscotch-common/src/helpers/auth/types/__tests__/digest.spec.ts b/packages/hoppscotch-common/src/helpers/auth/types/__tests__/digest.spec.ts new file mode 100644 index 00000000..9d404f6e --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/auth/types/__tests__/digest.spec.ts @@ -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: "<>", + password: "<>", + 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) + }) + }) +}) diff --git a/packages/hoppscotch-common/src/helpers/auth/types/__tests__/hawk.spec.ts b/packages/hoppscotch-common/src/helpers/auth/types/__tests__/hawk.spec.ts new file mode 100644 index 00000000..46b89875 --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/auth/types/__tests__/hawk.spec.ts @@ -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: "<>", + authKey: "<>", + 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: "<>", + authKey: "<>", + 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: "<>", + authKey: "<>", + 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, + }) + ) + }) + }) +}) diff --git a/packages/hoppscotch-common/src/helpers/auth/types/__tests__/jwt.spec.ts b/packages/hoppscotch-common/src/helpers/auth/types/__tests__/jwt.spec.ts new file mode 100644 index 00000000..032709f6 --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/auth/types/__tests__/jwt.spec.ts @@ -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: "<>", + privateKey: "", + algorithm: "HS256", + 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") + }) + }) +}) diff --git a/packages/hoppscotch-common/src/helpers/auth/types/__tests__/oauth2.spec.ts b/packages/hoppscotch-common/src/helpers/auth/types/__tests__/oauth2.spec.ts new file mode 100644 index 00000000..22f27975 --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/auth/types/__tests__/oauth2.spec.ts @@ -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: "<>", + clientSecret: "<>", + scopes: "read write", + 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 ") + }) + }) +}) diff --git a/packages/hoppscotch-common/src/helpers/auth/types/__tests__/test-utils.ts b/packages/hoppscotch-common/src/helpers/auth/types/__tests__/test-utils.ts new file mode 100644 index 00000000..625d9ee3 --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/auth/types/__tests__/test-utils.ts @@ -0,0 +1,121 @@ +import { Environment, makeRESTRequest } from "@hoppscotch/data" + +// Helper function to create base request +export const createBaseRequest = ( + overrides: Partial[0]> = {} +) => { + const baseRequest: Parameters[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", + }, +] diff --git a/packages/hoppscotch-common/src/helpers/auth/types/api-key.ts b/packages/hoppscotch-common/src/helpers/auth/types/api-key.ts index d6c0d249..16ba8c2a 100644 --- a/packages/hoppscotch-common/src/helpers/auth/types/api-key.ts +++ b/packages/hoppscotch-common/src/helpers/auth/types/api-key.ts @@ -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 { @@ -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 { diff --git a/packages/hoppscotch-common/src/helpers/auth/types/basic.ts b/packages/hoppscotch-common/src/helpers/auth/types/basic.ts index 0cabffc9..dce9d007 100644 --- a/packages/hoppscotch-common/src/helpers/auth/types/basic.ts +++ b/packages/hoppscotch-common/src/helpers/auth/types/basic.ts @@ -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 { diff --git a/packages/hoppscotch-common/src/helpers/auth/types/bearer.ts b/packages/hoppscotch-common/src/helpers/auth/types/bearer.ts index 980bcfdc..14c6dc6e 100644 --- a/packages/hoppscotch-common/src/helpers/auth/types/bearer.ts +++ b/packages/hoppscotch-common/src/helpers/auth/types/bearer.ts @@ -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 { diff --git a/packages/hoppscotch-common/src/helpers/auth/types/jwt.ts b/packages/hoppscotch-common/src/helpers/auth/types/jwt.ts index 86d92630..0e52d62d 100644 --- a/packages/hoppscotch-common/src/helpers/auth/types/jwt.ts +++ b/packages/hoppscotch-common/src/helpers/auth/types/jwt.ts @@ -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 { @@ -47,7 +45,6 @@ export async function generateJwtAuthHeaders( export async function generateJwtAuthParams( auth: HoppRESTAuth & { authType: "jwt" }, - request: HoppRESTRequest, envVars: Environment["variables"] ): Promise { if (auth.addTo !== "QUERY_PARAMS") return [] diff --git a/packages/hoppscotch-common/src/helpers/auth/types/oauth2.ts b/packages/hoppscotch-common/src/helpers/auth/types/oauth2.ts index 634b28dc..bcf08e0b 100644 --- a/packages/hoppscotch-common/src/helpers/auth/types/oauth2.ts +++ b/packages/hoppscotch-common/src/helpers/auth/types/oauth2.ts @@ -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 { @@ -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 {