@sudhi_s_developer_123/communicate-server 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +102 -0
- package/dist/client.d.ts +22 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/index.cjs +250 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +213 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/signer.d.ts +10 -0
- package/dist/signer.d.ts.map +1 -0
- package/dist/types.d.ts +89 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils.d.ts +19 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# @sudhi_s_developer_123/communicate-server
|
|
2
|
+
|
|
3
|
+
Official SDK for communicating with Our API. Modern, lightweight, and zero-dependency, featuring a **Secret Hashing** architecture for enhanced security.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @sudhi_s_developer_123/communicate-server
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Key Management
|
|
12
|
+
|
|
13
|
+
The SDK provides a utility to generate secure API credentials. In this model, the **Server** only needs to store a **hash** of the secret, while the **Client** uses the **raw** secret.
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { generateKeyPair } from "@sudhi_s_developer_123/communicate-server";
|
|
17
|
+
|
|
18
|
+
const { apiKey, apiSecret, secretHash } = generateKeyPair();
|
|
19
|
+
|
|
20
|
+
console.log(`API Key (Public): ${apiKey}`); // e.g. sk_...
|
|
21
|
+
console.log(`API Secret (Client-side usage): ${apiSecret}`); // e.g. ss_...
|
|
22
|
+
console.log(`Secret Hash (Server-side storage): ${secretHash}`);
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Client Usage
|
|
28
|
+
|
|
29
|
+
The `ApiClient` is used to make secure, signed requests. It uses the raw `apiSecret` and automatically derives the HMAC key for signing.
|
|
30
|
+
|
|
31
|
+
### Initializing the Client
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import {
|
|
35
|
+
ApiClient,
|
|
36
|
+
asApiKey,
|
|
37
|
+
asApiSecret,
|
|
38
|
+
asBaseURL
|
|
39
|
+
} from "@sudhi_s_developer_123/communicate-server";
|
|
40
|
+
|
|
41
|
+
const client = new ApiClient({
|
|
42
|
+
apiKey: asApiKey("sk_your-api-key"),
|
|
43
|
+
apiSecret: asApiSecret("ss_your-raw-api-secret"),
|
|
44
|
+
baseURL: asBaseURL("https://api.yourcompany.com"),
|
|
45
|
+
serverSecret: asServerSecret("optional-server-side-secret"),
|
|
46
|
+
timeout: 5000,
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Making Requests
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
// GET Request
|
|
54
|
+
const user = await client.get("/users/me");
|
|
55
|
+
|
|
56
|
+
// POST Request
|
|
57
|
+
const newItem = await client.post("/items", { name: "New Product" });
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Server Usage
|
|
63
|
+
|
|
64
|
+
The `ApiServer` is used to verify incoming requests. For maximum security, the server initialized with the **Secret Hash**, never needing to know the original raw secret.
|
|
65
|
+
|
|
66
|
+
### Initializing the Server
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { ApiServer, asSecretHash } from "@sudhi_s_developer_123/communicate-server";
|
|
70
|
+
|
|
71
|
+
const server = new ApiServer({
|
|
72
|
+
serverSecret: asServerSecret("optional-server-side-secret"),
|
|
73
|
+
tolerance: 300000, // 5 minutes drift allowed (default)
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Verifying a Request
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
const verification = server.verifyRequest({
|
|
81
|
+
signature: req.headers["x-signature"],
|
|
82
|
+
timestamp: req.headers["x-timestamp"],
|
|
83
|
+
requestId: req.headers["x-request-id"],
|
|
84
|
+
body: JSON.stringify(req.body),
|
|
85
|
+
secretHash: asSecretHash("your-stored-secret-hash"),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (verification.isValid) {
|
|
89
|
+
console.log("Request verified! Securely authenticated.");
|
|
90
|
+
} else {
|
|
91
|
+
console.error(`Verification failed: ${verification.error}`);
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Features
|
|
96
|
+
|
|
97
|
+
- **🛡️ Secret Hashing**: Server-side storage of secrets is limited to SHA-256 hashes.
|
|
98
|
+
- **🛡️ Type-Safe**: Branded types prevent mixing up sensitive strings like keys and secrets.
|
|
99
|
+
- **🚀 Lightweight**: Zero runtime dependencies, utilizing native `fetch` and `crypto`.
|
|
100
|
+
- **💻 Modern**: Built with TypeScript and supports ESM and CJS.
|
|
101
|
+
|
|
102
|
+
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ApiClientOptions, ApiHeaders, ApiPath, RequestData } from "./types.js";
|
|
2
|
+
export declare class ApiClient {
|
|
3
|
+
#private;
|
|
4
|
+
constructor(options: ApiClientOptions);
|
|
5
|
+
/**
|
|
6
|
+
* Send a GET request
|
|
7
|
+
*/
|
|
8
|
+
get<T>(path: ApiPath, headers?: ApiHeaders): Promise<T>;
|
|
9
|
+
/**
|
|
10
|
+
* Send a POST request
|
|
11
|
+
*/
|
|
12
|
+
post<T>(path: ApiPath, data?: RequestData, headers?: ApiHeaders): Promise<T>;
|
|
13
|
+
/**
|
|
14
|
+
* Send a PUT request
|
|
15
|
+
*/
|
|
16
|
+
put<T>(path: ApiPath, data?: RequestData, headers?: ApiHeaders): Promise<T>;
|
|
17
|
+
/**
|
|
18
|
+
* Send a DELETE request
|
|
19
|
+
*/
|
|
20
|
+
delete<T>(path: ApiPath, headers?: ApiHeaders): Promise<T>;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,gBAAgB,EAMhB,UAAU,EACV,OAAO,EAMP,WAAW,EAIZ,MAAM,YAAY,CAAC;AAEpB,qBAAa,SAAS;;gBAQR,OAAO,EAAE,gBAAgB;IAWrC;;OAEG;IACU,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC;IAIpE;;OAEG;IACU,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC;IAIzF;;OAEG;IACU,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC;IAIxF;;OAEG;IACU,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC;CAkExE"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
ApiClient: () => ApiClient,
|
|
24
|
+
ApiServer: () => ApiServer,
|
|
25
|
+
asApiKey: () => asApiKey,
|
|
26
|
+
asApiPath: () => asApiPath,
|
|
27
|
+
asApiSecret: () => asApiSecret,
|
|
28
|
+
asBaseURL: () => asBaseURL,
|
|
29
|
+
asSecretHash: () => asSecretHash,
|
|
30
|
+
asServerSecret: () => asServerSecret,
|
|
31
|
+
asTimeout: () => asTimeout,
|
|
32
|
+
deriveSecretHash: () => deriveSecretHash,
|
|
33
|
+
generateKeyPair: () => generateKeyPair
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/client.ts
|
|
38
|
+
var import_node_crypto3 = require("crypto");
|
|
39
|
+
|
|
40
|
+
// src/signer.ts
|
|
41
|
+
var import_node_crypto = require("crypto");
|
|
42
|
+
function generateSignature({
|
|
43
|
+
hashKey,
|
|
44
|
+
timestamp,
|
|
45
|
+
requestId,
|
|
46
|
+
body,
|
|
47
|
+
serverSecret
|
|
48
|
+
}) {
|
|
49
|
+
const payload = serverSecret ? `${timestamp}.${requestId}.${body}.${serverSecret}` : `${timestamp}.${requestId}.${body}`;
|
|
50
|
+
return (0, import_node_crypto.createHmac)("sha256", hashKey).update(payload).digest("hex");
|
|
51
|
+
}
|
|
52
|
+
function verifySignature(received, options) {
|
|
53
|
+
const computed = generateSignature(options);
|
|
54
|
+
const receivedBuffer = Buffer.from(received, "hex");
|
|
55
|
+
const computedBuffer = Buffer.from(computed, "hex");
|
|
56
|
+
if (receivedBuffer.length !== computedBuffer.length) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return (0, import_node_crypto.timingSafeEqual)(receivedBuffer, computedBuffer);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/utils.ts
|
|
63
|
+
var import_node_crypto2 = require("crypto");
|
|
64
|
+
|
|
65
|
+
// src/types.ts
|
|
66
|
+
var asApiKey = (v) => v;
|
|
67
|
+
var asApiSecret = (v) => v;
|
|
68
|
+
var asBaseURL = (v) => v;
|
|
69
|
+
var asTimeout = (v) => v;
|
|
70
|
+
var asApiPath = (v) => v;
|
|
71
|
+
var asSecretHash = (v) => v;
|
|
72
|
+
var asServerSecret = (v) => v;
|
|
73
|
+
|
|
74
|
+
// src/utils.ts
|
|
75
|
+
function generateKeyPair() {
|
|
76
|
+
const apiKey = asApiKey(`sk_${(0, import_node_crypto2.randomUUID)().replace(/-/g, "")}`);
|
|
77
|
+
const apiSecret = asApiSecret(`ss_${(0, import_node_crypto2.randomBytes)(32).toString("hex")}`);
|
|
78
|
+
const secretHash = deriveSecretHash(apiSecret);
|
|
79
|
+
return { apiKey, apiSecret, secretHash };
|
|
80
|
+
}
|
|
81
|
+
function deriveSecretHash(apiSecret) {
|
|
82
|
+
return asSecretHash(
|
|
83
|
+
(0, import_node_crypto2.createHash)("sha256").update(apiSecret).digest("hex")
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// src/client.ts
|
|
88
|
+
var ApiClient = class {
|
|
89
|
+
#apiKey;
|
|
90
|
+
#apiSecret;
|
|
91
|
+
#baseURL;
|
|
92
|
+
#timeout;
|
|
93
|
+
#headers;
|
|
94
|
+
#serverSecret;
|
|
95
|
+
constructor(options) {
|
|
96
|
+
this.#apiKey = options.apiKey;
|
|
97
|
+
this.#apiSecret = options.apiSecret;
|
|
98
|
+
this.#baseURL = options.baseURL.endsWith("/") ? options.baseURL.slice(0, -1) : options.baseURL;
|
|
99
|
+
this.#timeout = options.timeout ?? 1e4;
|
|
100
|
+
this.#headers = options.headers ?? {};
|
|
101
|
+
this.#serverSecret = options.serverSecret;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Send a GET request
|
|
105
|
+
*/
|
|
106
|
+
async get(path, headers) {
|
|
107
|
+
return this.#request("GET", path, void 0, headers);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Send a POST request
|
|
111
|
+
*/
|
|
112
|
+
async post(path, data, headers) {
|
|
113
|
+
return this.#request("POST", path, data, headers);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Send a PUT request
|
|
117
|
+
*/
|
|
118
|
+
async put(path, data, headers) {
|
|
119
|
+
return this.#request("PUT", path, data, headers);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Send a DELETE request
|
|
123
|
+
*/
|
|
124
|
+
async delete(path, headers) {
|
|
125
|
+
return this.#request("DELETE", path, void 0, headers);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Internal request handler using native fetch
|
|
129
|
+
*/
|
|
130
|
+
async #request(method, path, data, headers) {
|
|
131
|
+
const timestamp = Date.now().toString();
|
|
132
|
+
const requestId = (0, import_node_crypto3.randomUUID)();
|
|
133
|
+
const body = data ? JSON.stringify(data) : "";
|
|
134
|
+
const url = path.startsWith("/") ? `${this.#baseURL}${path}` : `${this.#baseURL}/${path}`;
|
|
135
|
+
const hashKey = deriveSecretHash(this.#apiSecret);
|
|
136
|
+
const signature = generateSignature({
|
|
137
|
+
hashKey,
|
|
138
|
+
timestamp,
|
|
139
|
+
requestId,
|
|
140
|
+
body,
|
|
141
|
+
serverSecret: this.#serverSecret
|
|
142
|
+
});
|
|
143
|
+
const controller = new AbortController();
|
|
144
|
+
const timeoutId = setTimeout(() => controller.abort(), this.#timeout);
|
|
145
|
+
try {
|
|
146
|
+
const response = await fetch(url, {
|
|
147
|
+
method,
|
|
148
|
+
headers: {
|
|
149
|
+
...this.#headers,
|
|
150
|
+
...headers,
|
|
151
|
+
"Content-Type": "application/json",
|
|
152
|
+
"Accept": "application/json",
|
|
153
|
+
"x-api-key": this.#apiKey,
|
|
154
|
+
"x-signature": signature,
|
|
155
|
+
"x-timestamp": timestamp,
|
|
156
|
+
"x-request-id": requestId
|
|
157
|
+
},
|
|
158
|
+
body: method !== "GET" && method !== "HEAD" && body ? body : void 0,
|
|
159
|
+
signal: controller.signal
|
|
160
|
+
});
|
|
161
|
+
clearTimeout(timeoutId);
|
|
162
|
+
if (!response.ok) {
|
|
163
|
+
const errorData = await response.json().catch(() => ({}));
|
|
164
|
+
const status = response.status;
|
|
165
|
+
const message = errorData.message || response.statusText;
|
|
166
|
+
throw new Error(`API Request failed (${status}): ${message}`);
|
|
167
|
+
}
|
|
168
|
+
return await response.json();
|
|
169
|
+
} catch (error) {
|
|
170
|
+
clearTimeout(timeoutId);
|
|
171
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
172
|
+
const timeoutMsg = `API Request timed out after ${this.#timeout}ms`;
|
|
173
|
+
throw new Error(timeoutMsg);
|
|
174
|
+
}
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// src/server.ts
|
|
181
|
+
var ApiServer = class {
|
|
182
|
+
#serverSecret;
|
|
183
|
+
#tolerance;
|
|
184
|
+
constructor(options) {
|
|
185
|
+
this.#serverSecret = options.serverSecret;
|
|
186
|
+
this.#tolerance = options.tolerance ?? 3e5;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Verify an incoming request's signature and timestamp.
|
|
190
|
+
*
|
|
191
|
+
* @param options - The extracted headers and body from the request
|
|
192
|
+
* @returns VerificationResult - object containing isValid and details
|
|
193
|
+
*/
|
|
194
|
+
verifyRequest(options) {
|
|
195
|
+
const { signature, timestamp, requestId, body, secretHash } = options;
|
|
196
|
+
if (!signature || !timestamp || !requestId) {
|
|
197
|
+
return {
|
|
198
|
+
isValid: false,
|
|
199
|
+
error: "MISSING_HEADERS",
|
|
200
|
+
message: "Missing one or more required auth headers (x-signature, x-timestamp, x-request-id)"
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
const now = Date.now();
|
|
204
|
+
const requestTime = parseInt(timestamp, 10);
|
|
205
|
+
if (isNaN(requestTime)) {
|
|
206
|
+
return {
|
|
207
|
+
isValid: false,
|
|
208
|
+
error: "TIMESTAMP_OUT_OF_RANGE",
|
|
209
|
+
message: "Invalid timestamp format"
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
const diff = Math.abs(now - requestTime);
|
|
213
|
+
if (diff > this.#tolerance) {
|
|
214
|
+
return {
|
|
215
|
+
isValid: false,
|
|
216
|
+
error: "TIMESTAMP_OUT_OF_RANGE",
|
|
217
|
+
message: `Request timestamp is outside the allowed drift window (drift: ${diff}ms, tolerance: ${this.#tolerance}ms)`
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
const isSignatureValid = verifySignature(signature, {
|
|
221
|
+
hashKey: secretHash,
|
|
222
|
+
timestamp,
|
|
223
|
+
requestId,
|
|
224
|
+
body,
|
|
225
|
+
serverSecret: this.#serverSecret
|
|
226
|
+
});
|
|
227
|
+
if (!isSignatureValid) {
|
|
228
|
+
return {
|
|
229
|
+
isValid: false,
|
|
230
|
+
error: "SIGNATURE_MISMATCH",
|
|
231
|
+
message: "The provided signature does not match the computed one"
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
return { isValid: true };
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
238
|
+
0 && (module.exports = {
|
|
239
|
+
ApiClient,
|
|
240
|
+
ApiServer,
|
|
241
|
+
asApiKey,
|
|
242
|
+
asApiPath,
|
|
243
|
+
asApiSecret,
|
|
244
|
+
asBaseURL,
|
|
245
|
+
asSecretHash,
|
|
246
|
+
asServerSecret,
|
|
247
|
+
asTimeout,
|
|
248
|
+
deriveSecretHash,
|
|
249
|
+
generateKeyPair
|
|
250
|
+
});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
3
|
+
|
|
4
|
+
// src/signer.ts
|
|
5
|
+
import { createHmac, timingSafeEqual } from "crypto";
|
|
6
|
+
function generateSignature({
|
|
7
|
+
hashKey,
|
|
8
|
+
timestamp,
|
|
9
|
+
requestId,
|
|
10
|
+
body,
|
|
11
|
+
serverSecret
|
|
12
|
+
}) {
|
|
13
|
+
const payload = serverSecret ? `${timestamp}.${requestId}.${body}.${serverSecret}` : `${timestamp}.${requestId}.${body}`;
|
|
14
|
+
return createHmac("sha256", hashKey).update(payload).digest("hex");
|
|
15
|
+
}
|
|
16
|
+
function verifySignature(received, options) {
|
|
17
|
+
const computed = generateSignature(options);
|
|
18
|
+
const receivedBuffer = Buffer.from(received, "hex");
|
|
19
|
+
const computedBuffer = Buffer.from(computed, "hex");
|
|
20
|
+
if (receivedBuffer.length !== computedBuffer.length) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return timingSafeEqual(receivedBuffer, computedBuffer);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// src/utils.ts
|
|
27
|
+
import { randomUUID, randomBytes, createHash } from "crypto";
|
|
28
|
+
|
|
29
|
+
// src/types.ts
|
|
30
|
+
var asApiKey = (v) => v;
|
|
31
|
+
var asApiSecret = (v) => v;
|
|
32
|
+
var asBaseURL = (v) => v;
|
|
33
|
+
var asTimeout = (v) => v;
|
|
34
|
+
var asApiPath = (v) => v;
|
|
35
|
+
var asSecretHash = (v) => v;
|
|
36
|
+
var asServerSecret = (v) => v;
|
|
37
|
+
|
|
38
|
+
// src/utils.ts
|
|
39
|
+
function generateKeyPair() {
|
|
40
|
+
const apiKey = asApiKey(`sk_${randomUUID().replace(/-/g, "")}`);
|
|
41
|
+
const apiSecret = asApiSecret(`ss_${randomBytes(32).toString("hex")}`);
|
|
42
|
+
const secretHash = deriveSecretHash(apiSecret);
|
|
43
|
+
return { apiKey, apiSecret, secretHash };
|
|
44
|
+
}
|
|
45
|
+
function deriveSecretHash(apiSecret) {
|
|
46
|
+
return asSecretHash(
|
|
47
|
+
createHash("sha256").update(apiSecret).digest("hex")
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/client.ts
|
|
52
|
+
var ApiClient = class {
|
|
53
|
+
#apiKey;
|
|
54
|
+
#apiSecret;
|
|
55
|
+
#baseURL;
|
|
56
|
+
#timeout;
|
|
57
|
+
#headers;
|
|
58
|
+
#serverSecret;
|
|
59
|
+
constructor(options) {
|
|
60
|
+
this.#apiKey = options.apiKey;
|
|
61
|
+
this.#apiSecret = options.apiSecret;
|
|
62
|
+
this.#baseURL = options.baseURL.endsWith("/") ? options.baseURL.slice(0, -1) : options.baseURL;
|
|
63
|
+
this.#timeout = options.timeout ?? 1e4;
|
|
64
|
+
this.#headers = options.headers ?? {};
|
|
65
|
+
this.#serverSecret = options.serverSecret;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Send a GET request
|
|
69
|
+
*/
|
|
70
|
+
async get(path, headers) {
|
|
71
|
+
return this.#request("GET", path, void 0, headers);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Send a POST request
|
|
75
|
+
*/
|
|
76
|
+
async post(path, data, headers) {
|
|
77
|
+
return this.#request("POST", path, data, headers);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Send a PUT request
|
|
81
|
+
*/
|
|
82
|
+
async put(path, data, headers) {
|
|
83
|
+
return this.#request("PUT", path, data, headers);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Send a DELETE request
|
|
87
|
+
*/
|
|
88
|
+
async delete(path, headers) {
|
|
89
|
+
return this.#request("DELETE", path, void 0, headers);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Internal request handler using native fetch
|
|
93
|
+
*/
|
|
94
|
+
async #request(method, path, data, headers) {
|
|
95
|
+
const timestamp = Date.now().toString();
|
|
96
|
+
const requestId = randomUUID2();
|
|
97
|
+
const body = data ? JSON.stringify(data) : "";
|
|
98
|
+
const url = path.startsWith("/") ? `${this.#baseURL}${path}` : `${this.#baseURL}/${path}`;
|
|
99
|
+
const hashKey = deriveSecretHash(this.#apiSecret);
|
|
100
|
+
const signature = generateSignature({
|
|
101
|
+
hashKey,
|
|
102
|
+
timestamp,
|
|
103
|
+
requestId,
|
|
104
|
+
body,
|
|
105
|
+
serverSecret: this.#serverSecret
|
|
106
|
+
});
|
|
107
|
+
const controller = new AbortController();
|
|
108
|
+
const timeoutId = setTimeout(() => controller.abort(), this.#timeout);
|
|
109
|
+
try {
|
|
110
|
+
const response = await fetch(url, {
|
|
111
|
+
method,
|
|
112
|
+
headers: {
|
|
113
|
+
...this.#headers,
|
|
114
|
+
...headers,
|
|
115
|
+
"Content-Type": "application/json",
|
|
116
|
+
"Accept": "application/json",
|
|
117
|
+
"x-api-key": this.#apiKey,
|
|
118
|
+
"x-signature": signature,
|
|
119
|
+
"x-timestamp": timestamp,
|
|
120
|
+
"x-request-id": requestId
|
|
121
|
+
},
|
|
122
|
+
body: method !== "GET" && method !== "HEAD" && body ? body : void 0,
|
|
123
|
+
signal: controller.signal
|
|
124
|
+
});
|
|
125
|
+
clearTimeout(timeoutId);
|
|
126
|
+
if (!response.ok) {
|
|
127
|
+
const errorData = await response.json().catch(() => ({}));
|
|
128
|
+
const status = response.status;
|
|
129
|
+
const message = errorData.message || response.statusText;
|
|
130
|
+
throw new Error(`API Request failed (${status}): ${message}`);
|
|
131
|
+
}
|
|
132
|
+
return await response.json();
|
|
133
|
+
} catch (error) {
|
|
134
|
+
clearTimeout(timeoutId);
|
|
135
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
136
|
+
const timeoutMsg = `API Request timed out after ${this.#timeout}ms`;
|
|
137
|
+
throw new Error(timeoutMsg);
|
|
138
|
+
}
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// src/server.ts
|
|
145
|
+
var ApiServer = class {
|
|
146
|
+
#serverSecret;
|
|
147
|
+
#tolerance;
|
|
148
|
+
constructor(options) {
|
|
149
|
+
this.#serverSecret = options.serverSecret;
|
|
150
|
+
this.#tolerance = options.tolerance ?? 3e5;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Verify an incoming request's signature and timestamp.
|
|
154
|
+
*
|
|
155
|
+
* @param options - The extracted headers and body from the request
|
|
156
|
+
* @returns VerificationResult - object containing isValid and details
|
|
157
|
+
*/
|
|
158
|
+
verifyRequest(options) {
|
|
159
|
+
const { signature, timestamp, requestId, body, secretHash } = options;
|
|
160
|
+
if (!signature || !timestamp || !requestId) {
|
|
161
|
+
return {
|
|
162
|
+
isValid: false,
|
|
163
|
+
error: "MISSING_HEADERS",
|
|
164
|
+
message: "Missing one or more required auth headers (x-signature, x-timestamp, x-request-id)"
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const now = Date.now();
|
|
168
|
+
const requestTime = parseInt(timestamp, 10);
|
|
169
|
+
if (isNaN(requestTime)) {
|
|
170
|
+
return {
|
|
171
|
+
isValid: false,
|
|
172
|
+
error: "TIMESTAMP_OUT_OF_RANGE",
|
|
173
|
+
message: "Invalid timestamp format"
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
const diff = Math.abs(now - requestTime);
|
|
177
|
+
if (diff > this.#tolerance) {
|
|
178
|
+
return {
|
|
179
|
+
isValid: false,
|
|
180
|
+
error: "TIMESTAMP_OUT_OF_RANGE",
|
|
181
|
+
message: `Request timestamp is outside the allowed drift window (drift: ${diff}ms, tolerance: ${this.#tolerance}ms)`
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
const isSignatureValid = verifySignature(signature, {
|
|
185
|
+
hashKey: secretHash,
|
|
186
|
+
timestamp,
|
|
187
|
+
requestId,
|
|
188
|
+
body,
|
|
189
|
+
serverSecret: this.#serverSecret
|
|
190
|
+
});
|
|
191
|
+
if (!isSignatureValid) {
|
|
192
|
+
return {
|
|
193
|
+
isValid: false,
|
|
194
|
+
error: "SIGNATURE_MISMATCH",
|
|
195
|
+
message: "The provided signature does not match the computed one"
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
return { isValid: true };
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
export {
|
|
202
|
+
ApiClient,
|
|
203
|
+
ApiServer,
|
|
204
|
+
asApiKey,
|
|
205
|
+
asApiPath,
|
|
206
|
+
asApiSecret,
|
|
207
|
+
asBaseURL,
|
|
208
|
+
asSecretHash,
|
|
209
|
+
asServerSecret,
|
|
210
|
+
asTimeout,
|
|
211
|
+
deriveSecretHash,
|
|
212
|
+
generateKeyPair
|
|
213
|
+
};
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ApiServerOptions, VerificationResult, VerifyRequestOptions } from "./types.js";
|
|
2
|
+
export declare class ApiServer {
|
|
3
|
+
#private;
|
|
4
|
+
constructor(options: ApiServerOptions);
|
|
5
|
+
/**
|
|
6
|
+
* Verify an incoming request's signature and timestamp.
|
|
7
|
+
*
|
|
8
|
+
* @param options - The extracted headers and body from the request
|
|
9
|
+
* @returns VerificationResult - object containing isValid and details
|
|
10
|
+
*/
|
|
11
|
+
verifyRequest(options: VerifyRequestOptions): VerificationResult;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EAIrB,MAAM,YAAY,CAAC;AAEpB,qBAAa,SAAS;;gBAIR,OAAO,EAAE,gBAAgB;IAKrC;;;;;OAKG;IACI,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,kBAAkB;CAoDxE"}
|
package/dist/signer.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SignerOptions, Signature } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Generate a signature using the derived HMAC key (the secret hash).
|
|
4
|
+
*/
|
|
5
|
+
export declare function generateSignature({ hashKey, timestamp, requestId, body, serverSecret, }: SignerOptions): Signature;
|
|
6
|
+
/**
|
|
7
|
+
* Verify a received signature matches the computed one
|
|
8
|
+
*/
|
|
9
|
+
export declare function verifySignature(received: Signature, options: SignerOptions): boolean;
|
|
10
|
+
//# sourceMappingURL=signer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signer.d.ts","sourceRoot":"","sources":["../src/signer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAiB,MAAM,YAAY,CAAC;AAE1E;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,SAAS,EACT,SAAS,EACT,IAAI,EACJ,YAAY,GACb,EAAE,aAAa,GAAG,SAAS,CAQ3B;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAWpF"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
declare const brand: unique symbol;
|
|
2
|
+
/**
|
|
3
|
+
* Utility for creating branded types.
|
|
4
|
+
* Makes it impossible to use one string in place of another (nominal typing).
|
|
5
|
+
*/
|
|
6
|
+
export type Brand<T, TBrand extends string> = T & {
|
|
7
|
+
[brand]: TBrand;
|
|
8
|
+
};
|
|
9
|
+
/** HTTP configuration */
|
|
10
|
+
export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD";
|
|
11
|
+
/** Identity and Secret types (Branded) */
|
|
12
|
+
export type ApiKey = Brand<string, "ApiKey">;
|
|
13
|
+
export type ApiSecret = Brand<string, "ApiSecret">;
|
|
14
|
+
/** Network and Timing types (Branded) */
|
|
15
|
+
export type BaseURL = Brand<string, "BaseURL">;
|
|
16
|
+
export type Timeout = Brand<number, "Timeout">;
|
|
17
|
+
export type ApiHeaders = Record<string, string>;
|
|
18
|
+
export type ApiPath = Brand<string, "ApiPath">;
|
|
19
|
+
export type RequestURL = Brand<string, "RequestURL">;
|
|
20
|
+
/** Signature and Auth types (Branded) */
|
|
21
|
+
export type Timestamp = Brand<string, "Timestamp">;
|
|
22
|
+
export type RequestId = Brand<string, "RequestId">;
|
|
23
|
+
export type RequestBody = Brand<string, "RequestBody">;
|
|
24
|
+
export type Signature = Brand<string, "Signature">;
|
|
25
|
+
export type SecretHash = Brand<string, "SecretHash">;
|
|
26
|
+
export type ServerSecret = Brand<string, "ServerSecret">;
|
|
27
|
+
export type SignerPayload = Brand<string, "SignerPayload">;
|
|
28
|
+
/** Data and Error types (Branded) */
|
|
29
|
+
export type RequestData = unknown;
|
|
30
|
+
export type StatusCode = Brand<number, "StatusCode">;
|
|
31
|
+
export type ErrorMessage = Brand<string, "ErrorMessage">;
|
|
32
|
+
export type StatusText = Brand<string, "StatusText">;
|
|
33
|
+
/** Casting helpers for creating branded types safely */
|
|
34
|
+
export declare const asApiKey: (v: string) => ApiKey;
|
|
35
|
+
export declare const asApiSecret: (v: string) => ApiSecret;
|
|
36
|
+
export declare const asBaseURL: (v: string) => BaseURL;
|
|
37
|
+
export declare const asTimeout: (v: number) => Timeout;
|
|
38
|
+
export declare const asApiPath: (v: string) => ApiPath;
|
|
39
|
+
export declare const asSecretHash: (v: string) => SecretHash;
|
|
40
|
+
export declare const asServerSecret: (v: string) => ServerSecret;
|
|
41
|
+
/** Client Configuration */
|
|
42
|
+
export interface ApiClientOptions {
|
|
43
|
+
apiKey: ApiKey;
|
|
44
|
+
apiSecret: ApiSecret;
|
|
45
|
+
baseURL: BaseURL;
|
|
46
|
+
/** Optional server-side secret for extra security layer */
|
|
47
|
+
serverSecret?: ServerSecret;
|
|
48
|
+
timeout?: Timeout;
|
|
49
|
+
headers?: ApiHeaders;
|
|
50
|
+
}
|
|
51
|
+
/** Server Configuration */
|
|
52
|
+
export interface ApiServerOptions {
|
|
53
|
+
/** Optional server-side secret for extra security layer */
|
|
54
|
+
serverSecret?: ServerSecret;
|
|
55
|
+
/** Max allowed clock drift in milliseconds. Defaults to 300,000 (5 mins). */
|
|
56
|
+
tolerance?: number;
|
|
57
|
+
}
|
|
58
|
+
/** Request Verification inputs */
|
|
59
|
+
export interface VerifyRequestOptions {
|
|
60
|
+
signature: Signature;
|
|
61
|
+
timestamp: Timestamp;
|
|
62
|
+
requestId: RequestId;
|
|
63
|
+
body: RequestBody;
|
|
64
|
+
/** The SHA-256 hash of the API secret for the user/client making the request */
|
|
65
|
+
secretHash: SecretHash;
|
|
66
|
+
}
|
|
67
|
+
/** Verification Success Result */
|
|
68
|
+
export interface VerificationSuccess {
|
|
69
|
+
isValid: true;
|
|
70
|
+
}
|
|
71
|
+
/** Verification Error Result */
|
|
72
|
+
export interface VerificationFailure {
|
|
73
|
+
isValid: false;
|
|
74
|
+
error: "SIGNATURE_MISMATCH" | "TIMESTAMP_OUT_OF_RANGE" | "MISSING_HEADERS";
|
|
75
|
+
message: ErrorMessage;
|
|
76
|
+
}
|
|
77
|
+
export type VerificationResult = VerificationSuccess | VerificationFailure;
|
|
78
|
+
/** Signature Engine inputs */
|
|
79
|
+
export interface SignerOptions {
|
|
80
|
+
/** The derived key for HMAC (the secret hash) */
|
|
81
|
+
hashKey: SecretHash;
|
|
82
|
+
timestamp: Timestamp;
|
|
83
|
+
requestId: RequestId;
|
|
84
|
+
body: RequestBody;
|
|
85
|
+
/** Optional server-side secret for extra security layer */
|
|
86
|
+
serverSecret?: ServerSecret;
|
|
87
|
+
}
|
|
88
|
+
export {};
|
|
89
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ApiKey, type ApiSecret, type SecretHash } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Generate a new API Key Pair.
|
|
4
|
+
*
|
|
5
|
+
* Returns:
|
|
6
|
+
* - apiKey: Public identifier
|
|
7
|
+
* - apiSecret: Private secret (should only be shown once)
|
|
8
|
+
* - secretHash: SHA-256 hash of the secret (stored on the server)
|
|
9
|
+
*/
|
|
10
|
+
export declare function generateKeyPair(): {
|
|
11
|
+
apiKey: ApiKey;
|
|
12
|
+
apiSecret: ApiSecret;
|
|
13
|
+
secretHash: SecretHash;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Derives a SecretHash from an ApiSecret using SHA-256
|
|
17
|
+
*/
|
|
18
|
+
export declare function deriveSecretHash(apiSecret: ApiSecret): SecretHash;
|
|
19
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,MAAM,EACX,KAAK,SAAS,EACd,KAAK,UAAU,EAEhB,MAAM,YAAY,CAAC;AAEpB;;;;;;;GAOG;AACH,wBAAgB,eAAe,IAAI;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,UAAU,CAAC;CACxB,CAMA;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,SAAS,GAAG,UAAU,CAMjE"}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sudhi_s_developer_123/communicate-server",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Official SDK for communicating with Our API",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup src/index.ts --format esm,cjs --clean && tsc --emitDeclarationOnly"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^25.5.2",
|
|
24
|
+
"tsup": "^8.5.1",
|
|
25
|
+
"typescript": "^6.0.2"
|
|
26
|
+
}
|
|
27
|
+
}
|