@calimero-network/mero-js 19.15.0 → 19.16.0
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/dist/crypto/internal.d.ts +11 -0
- package/dist/crypto/internal.js +21 -0
- package/dist/http-client/http-factory.d.ts +3 -0
- package/dist/http-client/http-factory.js +2 -0
- package/dist/http-client/http-types.d.ts +15 -0
- package/dist/http-client/web-client.js +37 -0
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +4 -4
- package/dist/index.cjs +74 -0
- package/dist/index.cjs.map +3 -3
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +74 -0
- package/dist/index.mjs.map +3 -3
- package/dist/request/caller-proof.d.ts +43 -0
- package/dist/request/caller-proof.js +36 -0
- package/dist/request/index.d.ts +2 -0
- package/dist/request/index.js +2 -0
- package/dist/request/proof-signer.d.ts +49 -0
- package/dist/request/proof-signer.js +59 -0
- package/package.json +1 -1
|
@@ -11,6 +11,17 @@
|
|
|
11
11
|
export declare function u32le(value: number): Uint8Array;
|
|
12
12
|
export declare function hex(bytes: Uint8Array): string;
|
|
13
13
|
export declare function fromHex(value: string, label: string, expectedBytes: number): Uint8Array;
|
|
14
|
+
/**
|
|
15
|
+
* Decode hex of an unknown length.
|
|
16
|
+
*
|
|
17
|
+
* The sized {@link fromHex} is the right default — every key and digest here
|
|
18
|
+
* has a fixed width, and checking it catches a truncated paste at the edge
|
|
19
|
+
* rather than deep inside a signature check. This one exists for the already-
|
|
20
|
+
* encoded structures whose length is genuinely variable: a device certificate,
|
|
21
|
+
* a login statement, a request signature. It still rejects odd-length and
|
|
22
|
+
* non-hex input, so the only check it gives up is the one that cannot apply.
|
|
23
|
+
*/
|
|
24
|
+
export declare function fromHexUnsized(value: string, label: string): Uint8Array;
|
|
14
25
|
export declare function u64le(value: number | bigint): Uint8Array;
|
|
15
26
|
export declare function concat(...parts: Uint8Array[]): Uint8Array;
|
|
16
27
|
/**
|
package/dist/crypto/internal.js
CHANGED
|
@@ -34,6 +34,27 @@ export function fromHex(value, label, expectedBytes) {
|
|
|
34
34
|
}
|
|
35
35
|
return bytes;
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Decode hex of an unknown length.
|
|
39
|
+
*
|
|
40
|
+
* The sized {@link fromHex} is the right default — every key and digest here
|
|
41
|
+
* has a fixed width, and checking it catches a truncated paste at the edge
|
|
42
|
+
* rather than deep inside a signature check. This one exists for the already-
|
|
43
|
+
* encoded structures whose length is genuinely variable: a device certificate,
|
|
44
|
+
* a login statement, a request signature. It still rejects odd-length and
|
|
45
|
+
* non-hex input, so the only check it gives up is the one that cannot apply.
|
|
46
|
+
*/
|
|
47
|
+
export function fromHexUnsized(value, label) {
|
|
48
|
+
const clean = value.trim();
|
|
49
|
+
if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length % 2 !== 0) {
|
|
50
|
+
throw new Error(`${label} must be an even number of hex characters`);
|
|
51
|
+
}
|
|
52
|
+
const bytes = new Uint8Array(clean.length / 2);
|
|
53
|
+
for (let i = 0; i < bytes.length; i += 1) {
|
|
54
|
+
bytes[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);
|
|
55
|
+
}
|
|
56
|
+
return bytes;
|
|
57
|
+
}
|
|
37
58
|
export function u64le(value) {
|
|
38
59
|
const out = new Uint8Array(8);
|
|
39
60
|
new DataView(out.buffer).setBigUint64(0, BigInt(value), true);
|
|
@@ -3,6 +3,7 @@ export declare function createHttpClient(transport: Transport): HttpClient;
|
|
|
3
3
|
export declare function createBrowserHttpClient(options: {
|
|
4
4
|
baseUrl: string;
|
|
5
5
|
getAuthToken?: () => Promise<string | undefined>;
|
|
6
|
+
getProof?: Transport['getProof'];
|
|
6
7
|
onTokenRefresh?: (newToken: string) => Promise<void>;
|
|
7
8
|
/**
|
|
8
9
|
* Callback to refresh the access token when a 401 error with 'token_expired' is detected.
|
|
@@ -25,6 +26,7 @@ export declare function createNodeHttpClient(options: {
|
|
|
25
26
|
baseUrl: string;
|
|
26
27
|
fetch?: typeof fetch;
|
|
27
28
|
getAuthToken?: () => Promise<string | undefined>;
|
|
29
|
+
getProof?: Transport['getProof'];
|
|
28
30
|
onTokenRefresh?: (newToken: string) => Promise<void>;
|
|
29
31
|
/**
|
|
30
32
|
* Callback to refresh the access token when a 401 error with 'token_expired' is detected.
|
|
@@ -47,6 +49,7 @@ export declare function createUniversalHttpClient(options: {
|
|
|
47
49
|
baseUrl: string;
|
|
48
50
|
fetch?: typeof fetch;
|
|
49
51
|
getAuthToken?: () => Promise<string | undefined>;
|
|
52
|
+
getProof?: Transport['getProof'];
|
|
50
53
|
onTokenRefresh?: (newToken: string) => Promise<void>;
|
|
51
54
|
/**
|
|
52
55
|
* Callback to refresh the access token when a 401 error with 'token_expired' is detected.
|
|
@@ -11,6 +11,7 @@ export function createBrowserHttpClient(options) {
|
|
|
11
11
|
fetch: (url, init) => globalThis.fetch(url, init),
|
|
12
12
|
baseUrl: options.baseUrl,
|
|
13
13
|
getAuthToken: options.getAuthToken,
|
|
14
|
+
getProof: options.getProof,
|
|
14
15
|
onTokenRefresh: options.onTokenRefresh,
|
|
15
16
|
refreshToken: options.refreshToken,
|
|
16
17
|
onAuthRevoked: options.onAuthRevoked,
|
|
@@ -42,6 +43,7 @@ export function createNodeHttpClient(options) {
|
|
|
42
43
|
: (url, init) => fetchImpl(url, init),
|
|
43
44
|
baseUrl: options.baseUrl,
|
|
44
45
|
getAuthToken: options.getAuthToken,
|
|
46
|
+
getProof: options.getProof,
|
|
45
47
|
onTokenRefresh: options.onTokenRefresh,
|
|
46
48
|
refreshToken: options.refreshToken,
|
|
47
49
|
onAuthRevoked: options.onAuthRevoked,
|
|
@@ -4,6 +4,21 @@ export interface Transport {
|
|
|
4
4
|
baseUrl: string;
|
|
5
5
|
defaultHeaders?: Record<string, string>;
|
|
6
6
|
getAuthToken?: () => Promise<string | undefined>;
|
|
7
|
+
/**
|
|
8
|
+
* Sign each request instead of, or alongside, presenting a session.
|
|
9
|
+
*
|
|
10
|
+
* Called per call with the method, the path the node will see, and the exact
|
|
11
|
+
* body — a proof commits to all three, so unlike `getAuthToken` it cannot be
|
|
12
|
+
* resolved once and reused. Returning `undefined` sends no proof header,
|
|
13
|
+
* which is how a client that only sometimes has a key stays usable.
|
|
14
|
+
*
|
|
15
|
+
* `createProofSigner` builds one of these from a credential and a key.
|
|
16
|
+
*/
|
|
17
|
+
getProof?: (request: {
|
|
18
|
+
method: string;
|
|
19
|
+
path: string;
|
|
20
|
+
body?: unknown;
|
|
21
|
+
}) => Promise<string | undefined>;
|
|
7
22
|
onTokenRefresh?: (newToken: string) => Promise<void>;
|
|
8
23
|
/**
|
|
9
24
|
* Callback to refresh the access token when a 401 error with 'token_expired' is detected.
|
|
@@ -88,6 +88,23 @@ function headersToRecord(headers) {
|
|
|
88
88
|
return record;
|
|
89
89
|
}
|
|
90
90
|
// Web Standards HTTP client implementation
|
|
91
|
+
/**
|
|
92
|
+
* The path a node will verify a request signature against.
|
|
93
|
+
*
|
|
94
|
+
* `new URL` drops the query and keeps any prefix the base URL contributed,
|
|
95
|
+
* which is exactly what reaches the server. The fallback covers a `url` that is
|
|
96
|
+
* already relative — it cannot be parsed, and signing the whole string with its
|
|
97
|
+
* query attached would be worse than signing the part before the `?`.
|
|
98
|
+
*/
|
|
99
|
+
function pathForProof(url) {
|
|
100
|
+
try {
|
|
101
|
+
return new URL(url).pathname;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
const query = url.indexOf('?');
|
|
105
|
+
return query === -1 ? url : url.slice(0, query);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
91
108
|
export class WebHttpClient {
|
|
92
109
|
constructor(transport) {
|
|
93
110
|
this.transport = transport;
|
|
@@ -234,6 +251,26 @@ export class WebHttpClient {
|
|
|
234
251
|
if (init?.keepalive !== undefined) {
|
|
235
252
|
requestInit.keepalive = init.keepalive;
|
|
236
253
|
}
|
|
254
|
+
// Signed here rather than in `buildHeaders`, which sees neither the method
|
|
255
|
+
// nor the body — and a proof commits to both. Signed AFTER the body is
|
|
256
|
+
// settled above, so what is hashed is what is sent.
|
|
257
|
+
//
|
|
258
|
+
// The path comes from the built URL's `pathname`, not from the `path`
|
|
259
|
+
// argument: that argument may carry a query string, and may be relative to
|
|
260
|
+
// a base URL that contributes its own prefix. The node verifies against the
|
|
261
|
+
// path it received, so the only safe source is the URL actually being
|
|
262
|
+
// fetched. A query string is deliberately not covered — core's `RequestSig`
|
|
263
|
+
// signs the path alone, and signing more here would fail every request.
|
|
264
|
+
if (this.transport.getProof) {
|
|
265
|
+
const proof = await this.transport.getProof({
|
|
266
|
+
method: requestInit.method ?? 'GET',
|
|
267
|
+
path: pathForProof(url),
|
|
268
|
+
body: requestInit.body,
|
|
269
|
+
});
|
|
270
|
+
if (proof) {
|
|
271
|
+
headersObj['X-Calimero-Proof'] = proof;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
237
274
|
try {
|
|
238
275
|
const response = await this.transport.fetch(url, requestInit);
|
|
239
276
|
if (!response.ok) {
|