From ccf3c6f83479b85ed1fe36f0fe3b0c09327af962 Mon Sep 17 00:00:00 2001 From: Anwarul Islam Date: Tue, 29 Apr 2025 15:21:16 +0600 Subject: [PATCH] feat: add support for `HAWK` authentication (#4694) Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com> --- .../src/__tests__/e2e/commands/test.spec.ts | 18 +++ .../collections/hawk-auth-success-coll.json | 43 ++++++ .../fixtures/environments/hawk-auth-envs.json | 47 ++++++ .../unit/fixtures/workspace-access.mock.ts | 26 ++-- .../hoppscotch-cli/src/utils/pre-request.ts | 40 +++++ packages/hoppscotch-common/locales/en.json | 3 +- packages/hoppscotch-common/package.json | 2 + .../src/components/http/Authorization.vue | 18 +++ .../http/authorization/AkamaiEG.vue | 2 +- .../components/http/authorization/HAWK.vue | 20 ++- .../src/helpers/utils/EffectiveURL.ts | 39 ++++- .../inspectors/request.inspector.ts | 16 ++ .../persistence/__tests__/__mocks__/index.ts | 4 +- .../hoppscotch-data/src/collection/index.ts | 6 +- .../hoppscotch-data/src/collection/v/7.ts | 37 +++++ packages/hoppscotch-data/src/index.ts | 2 + packages/hoppscotch-data/src/rest/index.ts | 14 +- packages/hoppscotch-data/src/rest/v/12.ts | 85 ++++++++++ .../hoppscotch-data/src/utils/akamai-eg.ts | 65 ++++++++ packages/hoppscotch-data/src/utils/hawk.ts | 146 ++++++++++++++++++ .../collections/collections.platform.ts | 12 +- .../src/platform/collections/desktop/index.ts | 12 +- .../src/platform/collections/web/index.ts | 12 +- pnpm-lock.yaml | 90 ++++++++++- 24 files changed, 709 insertions(+), 50 deletions(-) create mode 100644 packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/hawk-auth-success-coll.json create mode 100644 packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/hawk-auth-envs.json create mode 100644 packages/hoppscotch-data/src/collection/v/7.ts create mode 100644 packages/hoppscotch-data/src/rest/v/12.ts create mode 100644 packages/hoppscotch-data/src/utils/akamai-eg.ts create mode 100644 packages/hoppscotch-data/src/utils/hawk.ts diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/commands/test.spec.ts b/packages/hoppscotch-cli/src/__tests__/e2e/commands/test.spec.ts index 104e2531..e350e077 100644 --- a/packages/hoppscotch-cli/src/__tests__/e2e/commands/test.spec.ts +++ b/packages/hoppscotch-cli/src/__tests__/e2e/commands/test.spec.ts @@ -483,6 +483,24 @@ describe("hopp test [options] ", { timeout: 100000 }, () => { expect(error).toBeTruthy(); }); + + describe("HAWK Authentication", () => { + test("Correctly generates and attaches authorization headers to the request ", async () => { + const COLL_PATH = getTestJsonFilePath( + "hawk-auth-success-coll.json", + "collection" + ); + const ENV_PATH = getTestJsonFilePath( + "hawk-auth-envs.json", + "environment" + ); + + const args = `test ${COLL_PATH} -e ${ENV_PATH}`; + const { error } = await runCLI(args); + + expect(error).toBeNull(); + }); + }); }); describe("Test `hopp test --delay ` command:", () => { diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/hawk-auth-success-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/hawk-auth-success-coll.json new file mode 100644 index 00000000..fbf755fb --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/hawk-auth-success-coll.json @@ -0,0 +1,43 @@ +{ + "v": 3, + "name": "HAWK Auth (success state) - collection", + "folders": [], + "requests": [ + { + "v": "12", + "id": "cm0dm70cw000687bnxi830zz2", + "auth": { + "authType": "hawk", + "authActive": true, + "authId": "<>", + "authKey": "<>", + "algorithm": "<>", + "includePayloadHash": false, + "user": "<>", + "nonce": "<>", + "ext": "<>", + "app": "<>", + "dlg": "<>", + "timestamp": "<>" + }, + "body": { + "body": null, + "contentType": null + }, + "name": "hawk-auth-headers", + "method": "GET", + "params": [], + "headers": [], + "endpoint": "<>", + "testScript": "pw.test(\"Status code is 200\", ()=> { pw.expect(pw.response.status).toBe(200);});", + "preRequestScript": "", + "responses": {}, + "requestVariables": [] + } + ], + "auth": { + "authType": "inherit", + "authActive": true + }, + "headers": [] +} diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/hawk-auth-envs.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/hawk-auth-envs.json new file mode 100644 index 00000000..81ff74d6 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/hawk-auth-envs.json @@ -0,0 +1,47 @@ +{ + "v": 1, + "id": "cm0dsn3v70004p4qk3l9b7sjm", + "name": "HAWK Auth - environments", + "variables": [ + { + "key": "url", + "value": "https://postman-echo.com/auth/hawk" + }, + { + "key": "id", + "value": "dh37fgj492je" + }, + { + "key": "key", + "value": "werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn" + }, + { + "key": "algorithm", + "value": "sha256" + }, + { + "key": "user", + "value": "" + }, + { + "key": "nonce", + "value": "" + }, + { + "key": "ext", + "value": "" + }, + { + "key": "app", + "value": "" + }, + { + "key": "dlg", + "value": "" + }, + { + "key": "timestamp", + "value": "" + } + ] +} diff --git a/packages/hoppscotch-cli/src/__tests__/unit/fixtures/workspace-access.mock.ts b/packages/hoppscotch-cli/src/__tests__/unit/fixtures/workspace-access.mock.ts index b8ee0a70..d18e8eea 100644 --- a/packages/hoppscotch-cli/src/__tests__/unit/fixtures/workspace-access.mock.ts +++ b/packages/hoppscotch-cli/src/__tests__/unit/fixtures/workspace-access.mock.ts @@ -485,17 +485,17 @@ export const WORKSPACE_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Worksp export const TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: HoppCollection[] = [ { - v: 6, + v: 7, id: "clx1f86hv000010f8szcfya0t", name: "Multiple child collections with authorization & headers set at each level", folders: [ { - v: 6, + v: 7, id: "clx1fjgah000110f8a5bs68gd", name: "folder-1", folders: [ { - v: 6, + v: 7, id: "clx1fjwmm000410f8l1gkkr1a", name: "folder-11", folders: [], @@ -537,7 +537,7 @@ export const TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Hopp ], }, { - v: 6, + v: 7, id: "clx1fjyxm000510f8pv90dt43", name: "folder-12", folders: [], @@ -595,7 +595,7 @@ export const TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Hopp ], }, { - v: 6, + v: 7, id: "clx1fk1cv000610f88kc3aupy", name: "folder-13", folders: [], @@ -707,12 +707,12 @@ export const TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Hopp ], }, { - v: 6, + v: 7, id: "clx1fjk9o000210f8j0573pls", name: "folder-2", folders: [ { - v: 6, + v: 7, id: "clx1fk516000710f87sfpw6bo", name: "folder-21", folders: [], @@ -752,7 +752,7 @@ export const TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Hopp ], }, { - v: 6, + v: 7, id: "clx1fk72t000810f8gfwkpi5y", name: "folder-22", folders: [], @@ -810,7 +810,7 @@ export const TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Hopp ], }, { - v: 6, + v: 7, id: "clx1fk95g000910f8bunhaoo8", name: "folder-23", folders: [], @@ -915,12 +915,12 @@ export const TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Hopp ], }, { - v: 6, + v: 7, id: "clx1fjmlq000310f86o4d3w2o", name: "folder-3", folders: [ { - v: 6, + v: 7, id: "clx1iwq0p003e10f8u8zg0p85", name: "folder-31", folders: [], @@ -960,7 +960,7 @@ export const TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Hopp ], }, { - v: 6, + v: 7, id: "clx1izut7003m10f894ip59zg", name: "folder-32", folders: [], @@ -1018,7 +1018,7 @@ export const TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Hopp ], }, { - v: 6, + v: 7, id: "clx1j2ka9003q10f8cdbzpgpg", name: "folder-33", folders: [], diff --git a/packages/hoppscotch-cli/src/utils/pre-request.ts b/packages/hoppscotch-cli/src/utils/pre-request.ts index de456313..f8bca56e 100644 --- a/packages/hoppscotch-cli/src/utils/pre-request.ts +++ b/packages/hoppscotch-cli/src/utils/pre-request.ts @@ -32,6 +32,8 @@ import { generateDigestAuthHeader, } from "./auth/digest"; +import { calculateHawkHeader } from "@hoppscotch/data"; + /** * Runs pre-request-script runner over given request which extracts set ENVs and * applies them on current request to generate updated request. @@ -287,6 +289,44 @@ export async function getEffectiveRESTRequest( value: authHeaderValue, description: "", }); + } else if (request.auth.authType === "hawk") { + const { method, endpoint } = request; + + const hawkHeader = await calculateHawkHeader({ + url: parseTemplateString(endpoint, resolvedVariables), // URL + method: method, // HTTP method + id: parseTemplateString(request.auth.authId, resolvedVariables), + key: parseTemplateString(request.auth.authKey, resolvedVariables), + algorithm: request.auth.algorithm, + + // advanced parameters (optional) + includePayloadHash: request.auth.includePayloadHash, + nonce: request.auth.nonce + ? parseTemplateString(request.auth.nonce, resolvedVariables) + : undefined, + ext: request.auth.ext + ? parseTemplateString(request.auth.ext, resolvedVariables) + : undefined, + app: request.auth.app + ? parseTemplateString(request.auth.app, resolvedVariables) + : undefined, + dlg: request.auth.dlg + ? parseTemplateString(request.auth.dlg, resolvedVariables) + : undefined, + timestamp: request.auth.timestamp + ? parseInt( + parseTemplateString(request.auth.timestamp, resolvedVariables), + 10 + ) + : undefined, + }); + + effectiveFinalHeaders.push({ + active: true, + key: "Authorization", + value: hawkHeader, + description: "", + }); } } diff --git a/packages/hoppscotch-common/locales/en.json b/packages/hoppscotch-common/locales/en.json index 5aed1a5a..9f975c53 100644 --- a/packages/hoppscotch-common/locales/en.json +++ b/packages/hoppscotch-common/locales/en.json @@ -680,7 +680,8 @@ "localaccess_unsupported": "Current interceptor does not support local access, please consider using Agent, Extension interceptors or the Desktop App" }, "auth": { - "digest": "Agent interceptor or the Hoppscotch Desktop app are recommended when using Digest Authorization." + "digest": "Agent interceptor or the Hoppscotch Desktop app are recommended when using Digest Authorization.", + "hawk": "Agent interceptor or the Hoppscotch Desktop app are recommended when using Hawk Authorization." }, "body": { "binary": "Sending binary data via the current interceptor is not supported yet." diff --git a/packages/hoppscotch-common/package.json b/packages/hoppscotch-common/package.json index bea0b084..536dcd09 100644 --- a/packages/hoppscotch-common/package.json +++ b/packages/hoppscotch-common/package.json @@ -48,6 +48,7 @@ "@shopify/lang-jsonc": "1.0.0", "@tauri-apps/plugin-store": "2.2.0", "@types/markdown-it": "14.1.2", + "@types/hawk": "9.0.6", "@unhead/vue": "1.11.10", "@urql/core": "5.0.6", "@urql/devtools": "2.0.3", @@ -66,6 +67,7 @@ "graphql": "16.9.0", "graphql-language-service-interface": "2.10.2", "graphql-tag": "2.12.6", + "hawk": "9.0.2", "insomnia-importers": "3.6.0", "io-ts": "2.2.21", "js-md5": "0.8.3", diff --git a/packages/hoppscotch-common/src/components/http/Authorization.vue b/packages/hoppscotch-common/src/components/http/Authorization.vue index f09557b5..9b9773e9 100644 --- a/packages/hoppscotch-common/src/components/http/Authorization.vue +++ b/packages/hoppscotch-common/src/components/http/Authorization.vue @@ -151,6 +151,9 @@
+
+ +
@@ -193,6 +196,7 @@ import { HoppRESTAuth, HoppRESTAuthAWSSignature, HoppRESTAuthDigest, + HoppRESTAuthHAWK, HoppRESTAuthOAuth2, } from "@hoppscotch/data" @@ -265,6 +269,15 @@ const selectAWSSignatureAuthType = () => { } } +const selectHAWKAuthType = () => { + const { algorithm = "sha256" } = auth.value as HoppRESTAuthHAWK + auth.value = { + ...auth.value, + authType: "hawk", + algorithm, + } as HoppRESTAuth +} + const selectDigestAuthType = () => { const { username = "", @@ -318,6 +331,11 @@ const authTypes: AuthType[] = [ label: "AWS Signature", handler: selectAWSSignatureAuthType, }, + { + key: "hawk", + label: "HAWK", + handler: selectHAWKAuthType, + }, ] const authType = pluckRef(auth, "authType") diff --git a/packages/hoppscotch-common/src/components/http/authorization/AkamaiEG.vue b/packages/hoppscotch-common/src/components/http/authorization/AkamaiEG.vue index 99a43f09..2ae682ec 100644 --- a/packages/hoppscotch-common/src/components/http/authorization/AkamaiEG.vue +++ b/packages/hoppscotch-common/src/components/http/authorization/AkamaiEG.vue @@ -69,7 +69,7 @@
- + -
+
-
- +
+ +

+ {{ t("authorization.advance_config_description") }} +

-
+ +