diff --git a/package-lock.json b/package-lock.json index dc959ea8..282ce0b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,16 +8,17 @@ "name": "yaak-app", "version": "0.0.0", "workspaces": [ + "packages/common-lib", "packages/plugin-runtime", "packages/plugin-runtime-types", - "packages/common-lib", + "plugins/action-copy-curl", + "plugins/action-copy-grpcurl", "plugins/auth-apikey", + "plugins/auth-aws", "plugins/auth-basic", "plugins/auth-bearer", "plugins/auth-jwt", "plugins/auth-oauth2", - "plugins/action-copy-curl", - "plugins/action-copy-grpcurl", "plugins/filter-jsonpath", "plugins/filter-xpath", "plugins/importer-curl", @@ -26,7 +27,6 @@ "plugins/importer-postman", "plugins/importer-yaak", "plugins/template-function-cookie", - "plugins/template-function-timestamp", "plugins/template-function-encode", "plugins/template-function-fs", "plugins/template-function-hash", @@ -35,13 +35,14 @@ "plugins/template-function-regex", "plugins/template-function-request", "plugins/template-function-response", + "plugins/template-function-timestamp", "plugins/template-function-uuid", "plugins/template-function-xml", "plugins/themes-yaak", "src-tauri", "src-tauri/yaak-crypto", - "src-tauri/yaak-git", "src-tauri/yaak-fonts", + "src-tauri/yaak-git", "src-tauri/yaak-license", "src-tauri/yaak-mac-window", "src-tauri/yaak-models", @@ -3377,6 +3378,16 @@ "@tauri-apps/api": "^2.8.0" } }, + "node_modules/@types/aws4": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@types/aws4/-/aws4-1.11.6.tgz", + "integrity": "sha512-5CnVUkHNyLGpD9AnOcK66YyP0qvIh6nhJJoeK8zSl5YKikUcUbdB7SlHevUYVqicgeh6j5AJa1qa/h08dSZHoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -4112,6 +4123,10 @@ "resolved": "plugins/auth-apikey", "link": true }, + "node_modules/@yaak/auth-aws": { + "resolved": "plugins/auth-aws", + "link": true + }, "node_modules/@yaak/auth-basic": { "resolved": "plugins/auth-basic", "link": true @@ -4887,6 +4902,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "license": "MIT" + }, "node_modules/axe-core": { "version": "4.10.3", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz", @@ -18823,6 +18844,16 @@ "name": "@yaak/auth-apikey", "version": "0.1.0" }, + "plugins/auth-aws": { + "name": "@yaak/auth-aws", + "version": "0.1.0", + "dependencies": { + "aws4": "^1.13.2" + }, + "devDependencies": { + "@types/aws4": "^1.11.6" + } + }, "plugins/auth-basic": { "name": "@yaak/auth-basic", "version": "0.1.0" diff --git a/package.json b/package.json index 6d8e556c..631d00cc 100644 --- a/package.json +++ b/package.json @@ -7,16 +7,17 @@ "url": "git+https://github.com/mountain-loop/yaak.git" }, "workspaces": [ + "packages/common-lib", "packages/plugin-runtime", "packages/plugin-runtime-types", - "packages/common-lib", + "plugins/action-copy-curl", + "plugins/action-copy-grpcurl", "plugins/auth-apikey", + "plugins/auth-aws", "plugins/auth-basic", "plugins/auth-bearer", "plugins/auth-jwt", "plugins/auth-oauth2", - "plugins/action-copy-curl", - "plugins/action-copy-grpcurl", "plugins/filter-jsonpath", "plugins/filter-xpath", "plugins/importer-curl", @@ -25,7 +26,6 @@ "plugins/importer-postman", "plugins/importer-yaak", "plugins/template-function-cookie", - "plugins/template-function-timestamp", "plugins/template-function-encode", "plugins/template-function-fs", "plugins/template-function-hash", @@ -34,13 +34,14 @@ "plugins/template-function-regex", "plugins/template-function-request", "plugins/template-function-response", + "plugins/template-function-timestamp", "plugins/template-function-uuid", "plugins/template-function-xml", "plugins/themes-yaak", "src-tauri", "src-tauri/yaak-crypto", - "src-tauri/yaak-git", "src-tauri/yaak-fonts", + "src-tauri/yaak-git", "src-tauri/yaak-license", "src-tauri/yaak-mac-window", "src-tauri/yaak-models", diff --git a/plugins/auth-aws/README.md b/plugins/auth-aws/README.md new file mode 100644 index 00000000..bdc546b9 --- /dev/null +++ b/plugins/auth-aws/README.md @@ -0,0 +1,49 @@ +# AWS Signature Version 4 Auth + +A plugin for authenticating AWS-compatible requests using the +[AWS Signature Version 4 signing process](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html). +This enables secure, signed requests to AWS services (or any S3-compatible APIs like +Cloudflare R2). + +![Screenshot of AWS SigV4 UI](screenshot.png) + +## Overview + +This plugin provides AWS Signature authentication for API requests in Yaak. SigV4 is used +by nearly all AWS APIs to verify the authenticity and integrity of requests using +cryptographic signatures. + +With this plugin, you can securely sign requests to AWS services such as S3, STS, Lambda, +API Gateway, DynamoDB, and more. You can also authenticate against S3-compatible services +like **Cloudflare R2**, **MinIO**, or **Wasabi**. + +## How AWS Signature Version 4 Works + +SigV4 signs requests by creating a hash of key request components (method, URL, headers, +and optionally the payload) using your AWS credentials. The resulting HMAC signature is +added in the `Authorization` header along with credential scope metadata. + +Example header: + +``` +Authorization: AWS4-HMAC-SHA256 Credential=AKIA…/20251011/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-date, Signature=abcdef123456… +``` + +Each request must include a timestamp (`X-Amz-Date`) and may include a session token if +using temporary credentials. + +## Configuration + +The plugin presents the following fields: + +- **Access Key ID** – Your AWS access key identifier +- **Secret Access Key** – The secret associated with the access key +- **Session Token** *(optional)* – Used for temporary or assumed-role credentials (treated as secret) +- **Region** – AWS region (e.g., `us-east-1`) +- **Service** – AWS service identifier (e.g., `sts`, `s3`, `execute-api`) + +## Usage + +1. Configure a request, folder, or workspace to use **AWS SigV4 Authentication** +2. Enter your AWS credentials and target service/region +3. The plugin will automatically sign outgoing requests with valid SigV4 headers diff --git a/plugins/auth-aws/package.json b/plugins/auth-aws/package.json new file mode 100644 index 00000000..4af53627 --- /dev/null +++ b/plugins/auth-aws/package.json @@ -0,0 +1,23 @@ +{ + "name": "@yaak/auth-aws", + "displayName": "AWS SigV4", + "description": "Authenticate requests using AWS SigV4 signing", + "repository": { + "type": "git", + "url": "https://github.com/mountain-loop/yaak.git", + "directory": "plugins/auth-aws" + }, + "private": true, + "version": "0.1.0", + "scripts": { + "build": "yaakcli build", + "dev": "yaakcli dev", + "lint": "tsc --noEmit && eslint . --ext .ts,.tsx" + }, + "dependencies": { + "aws4": "^1.13.2" + }, + "devDependencies": { + "@types/aws4": "^1.11.6" + } +} diff --git a/plugins/auth-aws/screenshot.png b/plugins/auth-aws/screenshot.png new file mode 100644 index 00000000..309f7a02 Binary files /dev/null and b/plugins/auth-aws/screenshot.png differ diff --git a/plugins/auth-aws/src/index.ts b/plugins/auth-aws/src/index.ts new file mode 100644 index 00000000..560e4815 --- /dev/null +++ b/plugins/auth-aws/src/index.ts @@ -0,0 +1,97 @@ +import type { CallHttpAuthenticationResponse } from '@yaakapp-internal/plugins'; +import type { PluginDefinition } from '@yaakapp/api'; +import aws4 from 'aws4'; +import type { Request } from 'aws4'; +import { URL } from 'node:url'; + +export const plugin: PluginDefinition = { + authentication: { + name: 'auth-aws-sig-v4', + label: 'AWS Signature', + shortLabel: 'AWS v4', + args: [ + { name: 'accessKeyId', label: 'Access Key ID', type: 'text', password: true }, + { + name: 'secretAccessKey', + label: 'Secret Access Key', + type: 'text', + password: true, + }, + { + name: 'service', + label: 'Service Name', + type: 'text', + defaultValue: 'sts', + placeholder: 'sts', + description: 'The service that is receiving the request (sts, s3, sqs, ...)', + }, + { + name: 'region', + label: 'Region', + type: 'text', + placeholder: 'us-east-1', + description: 'The region that is receiving the request (defaults to us-east-1)', + optional: true, + }, + { + name: 'sessionToken', + label: 'Session Token', + type: 'text', + password: true, + optional: true, + description: 'Only required if you are using temporary credentials', + }, + ], + onApply(_ctx, { values, ...args }): CallHttpAuthenticationResponse { + const accessKeyId = String(values.accessKeyId || ''); + const secretAccessKey = String(values.secretAccessKey || ''); + const sessionToken = String(values.sessionToken || '') || undefined; + + const url = new URL(args.url); + + const headers: NonNullable = {}; + for (const headerName of ['content-type', 'host', 'x-amz-date', 'x-amz-security-token']) { + const v = args.headers.find((h) => h.name.toLowerCase() === headerName); + if (v != null) { + headers[headerName] = v.value; + } + } + + // TODO: Support body signing here + headers['x-amz-content-sha256'] = 'UNSIGNED-PAYLOAD'; + + const signature = aws4.sign( + { + host: url.host, + method: args.method, + path: url.pathname + (url.search || '') || undefined, + service: String(values.service || 'sts') || undefined, + region: String(values.region || 'us-east-1') || undefined, + headers, + }, + { + accessKeyId, + secretAccessKey, + sessionToken, + }, + ); + + // After signing, aws4 will set: + // - opts.headers["Authorization"] + // - opts.headers["X-Amz-Date"] + // - optionally content sha256 header etc + + console.log('ADDING STUFF', signature); + + if (signature.headers == null) { + return {}; + } + + return { + setHeaders: Object.entries(signature.headers) + .filter(([name]) => name !== 'content-type') // Don't add this because we already have it + .map(([name, value]) => ({ name, value: String(value || '') })), + }; + }, + }, +}; diff --git a/plugins/auth-aws/tsconfig.json b/plugins/auth-aws/tsconfig.json new file mode 100644 index 00000000..4082f16a --- /dev/null +++ b/plugins/auth-aws/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig.json" +}