@calimero-network/mero-js 25.3.0 → 25.3.2
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 +3 -3
- package/dist/auth/index.d.ts +11 -2
- package/dist/auth/index.js +20 -3
- package/dist/cloud/enrol-redirect.d.ts +10 -24
- package/dist/cloud/enrol-redirect.js +16 -8
- package/dist/cloud/link-state.d.ts +5 -1
- package/dist/cloud/link-state.js +28 -21
- package/dist/events/sse.js +20 -8
- package/dist/http-client/http-types.d.ts +1 -1
- package/dist/http-client/web-client.d.ts +7 -0
- package/dist/http-client/web-client.js +41 -25
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +4 -4
- package/dist/index.cjs +410 -364
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +410 -364
- package/dist/index.mjs.map +4 -4
- package/dist/mero-js.d.ts +2 -2
- package/dist/mero-js.js +2 -2
- package/dist/request/proof-signer.d.ts +1 -1
- package/dist/request/request-sig.d.ts +9 -9
- package/dist/request/request-sig.js +7 -8
- package/dist/request-proof/authorizer.d.ts +3 -10
- package/dist/request-proof/authorizer.js +5 -10
- package/dist/request-proof/request-proof.d.ts +3 -6
- package/dist/request-proof/request-proof.js +4 -20
- package/package.json +1 -1
package/dist/mero-js.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { AuthApiClient } from './auth-api/index.js';
|
|
|
2
2
|
import type { AdminApiClient } from './admin-api/index.js';
|
|
3
3
|
import type { TokenStore } from './token-store/index.js';
|
|
4
4
|
import type { ProofRequest, ProofSignerOptions } from './request/index.js';
|
|
5
|
-
import type { AuthCallbackResult, AuthLoginOptions } from './auth/index.js';
|
|
5
|
+
import type { AuthCallbackResult, AuthLoginOptions, ParseAuthCallbackOptions } from './auth/index.js';
|
|
6
6
|
import { RpcClient } from './rpc/index.js';
|
|
7
7
|
import { SseClient } from './events/sse.js';
|
|
8
8
|
import { WsClient } from './events/ws.js';
|
|
@@ -219,7 +219,7 @@ export declare class MeroJs {
|
|
|
219
219
|
/**
|
|
220
220
|
* Parse an auth callback URL hash fragment (static utility)
|
|
221
221
|
*/
|
|
222
|
-
static parseAuthCallback(url: string): AuthCallbackResult | null;
|
|
222
|
+
static parseAuthCallback(url: string, options?: ParseAuthCallbackOptions): AuthCallbackResult | null;
|
|
223
223
|
/**
|
|
224
224
|
* Build an auth login URL (static utility)
|
|
225
225
|
*/
|
package/dist/mero-js.js
CHANGED
|
@@ -451,8 +451,8 @@ export class MeroJs {
|
|
|
451
451
|
/**
|
|
452
452
|
* Parse an auth callback URL hash fragment (static utility)
|
|
453
453
|
*/
|
|
454
|
-
static parseAuthCallback(url) {
|
|
455
|
-
return parseAuthCallback(url);
|
|
454
|
+
static parseAuthCallback(url, options) {
|
|
455
|
+
return parseAuthCallback(url, options);
|
|
456
456
|
}
|
|
457
457
|
/**
|
|
458
458
|
* Build an auth login URL (static utility)
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
export interface ProofRequest {
|
|
12
12
|
/** `GET`, `POST`, … exactly as it will be sent. */
|
|
13
13
|
method: string;
|
|
14
|
-
/** The
|
|
14
|
+
/** The path and query the node will see, as `signedPath` reads them. */
|
|
15
15
|
path: string;
|
|
16
16
|
/** The exact bytes that will be sent, if any. */
|
|
17
17
|
body?: unknown;
|
|
@@ -18,14 +18,13 @@
|
|
|
18
18
|
* three. The node's verifier handles both, so which you produce here is a
|
|
19
19
|
* question about where your key lives, not about what the node accepts.
|
|
20
20
|
*
|
|
21
|
-
* **What it binds.** Method, path and a hash of the body. A
|
|
22
|
-
* `GET /a` is not a signature for `POST /a`, for
|
|
23
|
-
* with different
|
|
21
|
+
* **What it binds.** Method, path and query, and a hash of the body. A
|
|
22
|
+
* signature for `GET /a?x=1` is not a signature for `POST /a?x=1`, for
|
|
23
|
+
* `GET /a?x=2`, for `GET /b?x=1`, or for the same call with a different body.
|
|
24
24
|
*
|
|
25
|
-
* **The path
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* Anything that must be bound belongs in the body.
|
|
25
|
+
* **The path includes the query string**, exactly as it goes on the request
|
|
26
|
+
* line: no re-encoding, no reordering. A proxy between you and the node must
|
|
27
|
+
* forward both unchanged, or the node refuses the proof.
|
|
29
28
|
*
|
|
30
29
|
* **The method is not normalized.** `HEAD` and `GET` are the same *permission*
|
|
31
30
|
* and the node's permission layer folds them, but they are different requests.
|
|
@@ -43,6 +42,7 @@
|
|
|
43
42
|
* notice this file depends on both. This module's test asserts the same vectors.
|
|
44
43
|
*/
|
|
45
44
|
import { fromHex } from "../crypto/internal.js";
|
|
45
|
+
export declare const SIGN_DOMAIN: Uint8Array;
|
|
46
46
|
/**
|
|
47
47
|
* Commit to a request body.
|
|
48
48
|
*
|
|
@@ -59,8 +59,8 @@ export interface RequestSigInput {
|
|
|
59
59
|
*/
|
|
60
60
|
method: string;
|
|
61
61
|
/**
|
|
62
|
-
* The path,
|
|
63
|
-
* `/admin-api/
|
|
62
|
+
* The path and query, exactly as on the request line:
|
|
63
|
+
* `/admin-api/blobs?context_id=…`. Use `signedPath` to take it from a URL.
|
|
64
64
|
*/
|
|
65
65
|
path: string;
|
|
66
66
|
/**
|
|
@@ -18,14 +18,13 @@
|
|
|
18
18
|
* three. The node's verifier handles both, so which you produce here is a
|
|
19
19
|
* question about where your key lives, not about what the node accepts.
|
|
20
20
|
*
|
|
21
|
-
* **What it binds.** Method, path and a hash of the body. A
|
|
22
|
-
* `GET /a` is not a signature for `POST /a`, for
|
|
23
|
-
* with different
|
|
21
|
+
* **What it binds.** Method, path and query, and a hash of the body. A
|
|
22
|
+
* signature for `GET /a?x=1` is not a signature for `POST /a?x=1`, for
|
|
23
|
+
* `GET /a?x=2`, for `GET /b?x=1`, or for the same call with a different body.
|
|
24
24
|
*
|
|
25
|
-
* **The path
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* Anything that must be bound belongs in the body.
|
|
25
|
+
* **The path includes the query string**, exactly as it goes on the request
|
|
26
|
+
* line: no re-encoding, no reordering. A proxy between you and the node must
|
|
27
|
+
* forward both unchanged, or the node refuses the proof.
|
|
29
28
|
*
|
|
30
29
|
* **The method is not normalized.** `HEAD` and `GET` are the same *permission*
|
|
31
30
|
* and the node's permission layer folds them, but they are different requests.
|
|
@@ -43,7 +42,7 @@
|
|
|
43
42
|
* notice this file depends on both. This module's test asserts the same vectors.
|
|
44
43
|
*/
|
|
45
44
|
import { concat, domainHash, fromHex, hex, importSigningKey, u32le, u64le, } from "../crypto/internal.js";
|
|
46
|
-
const SIGN_DOMAIN = new TextEncoder().encode("calimero.auth.request.
|
|
45
|
+
export const SIGN_DOMAIN = new TextEncoder().encode("calimero.auth.request.v2");
|
|
47
46
|
/**
|
|
48
47
|
* Its own domain, distinct from the signing one.
|
|
49
48
|
*
|
|
@@ -13,7 +13,7 @@ export interface ProofCredential {
|
|
|
13
13
|
/** One request, as the proof has to see it. */
|
|
14
14
|
export interface RequestToAuthorize {
|
|
15
15
|
method: string;
|
|
16
|
-
/** Path
|
|
16
|
+
/** Path and query, as `signedPath` reads them from the URL being fetched. */
|
|
17
17
|
path: string;
|
|
18
18
|
/** The body exactly as it will be sent, if there is one. */
|
|
19
19
|
body?: string | Uint8Array;
|
|
@@ -36,13 +36,6 @@ export type RequestAuthorizer = (request: RequestToAuthorize) => Promise<Record<
|
|
|
36
36
|
* in the wrong place.
|
|
37
37
|
*/
|
|
38
38
|
export declare function createProofAuthorizer(credential: ProofCredential): RequestAuthorizer;
|
|
39
|
-
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* Callers hold a base URL and a relative path; the proof commits to the path the
|
|
43
|
-
* node will see, which is their join with the query dropped. Deriving it from
|
|
44
|
-
* the assembled URL rather than concatenating strings is what keeps a base URL
|
|
45
|
-
* carrying its own prefix (`…/admin-api`) from signing a path the node never
|
|
46
|
-
* receives.
|
|
47
|
-
*/
|
|
39
|
+
/** The path and query a proof signs, read from the parsed URL: the serialization
|
|
40
|
+
* `fetch` puts on the request line, base-URL prefix and escapes included. */
|
|
48
41
|
export declare function signedPath(url: string): string;
|
|
@@ -51,15 +51,10 @@ export function createProofAuthorizer(credential) {
|
|
|
51
51
|
return { [REQUEST_PROOF_HEADER]: proof };
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
|
-
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* Callers hold a base URL and a relative path; the proof commits to the path the
|
|
58
|
-
* node will see, which is their join with the query dropped. Deriving it from
|
|
59
|
-
* the assembled URL rather than concatenating strings is what keeps a base URL
|
|
60
|
-
* carrying its own prefix (`…/admin-api`) from signing a path the node never
|
|
61
|
-
* receives.
|
|
62
|
-
*/
|
|
54
|
+
/** The path and query a proof signs, read from the parsed URL: the serialization
|
|
55
|
+
* `fetch` puts on the request line, base-URL prefix and escapes included. */
|
|
63
56
|
export function signedPath(url) {
|
|
64
|
-
|
|
57
|
+
// A relative URL resolves as `fetch` resolves it: against the page, if any.
|
|
58
|
+
const parsed = new URL(url, globalThis.location?.href ?? 'http://localhost');
|
|
59
|
+
return parsed.pathname + parsed.search;
|
|
65
60
|
}
|
|
@@ -11,11 +11,8 @@ export interface RequestProofInput {
|
|
|
11
11
|
*/
|
|
12
12
|
method: string;
|
|
13
13
|
/**
|
|
14
|
-
* The path,
|
|
15
|
-
*
|
|
16
|
-
* Excluded because a proxy may legitimately rewrite a query, and a signature
|
|
17
|
-
* over bytes something else may change fails for reasons the caller cannot
|
|
18
|
-
* see. Anything that must be bound belongs in the body.
|
|
14
|
+
* The path and query, exactly as on the request line, with no re-encoding or
|
|
15
|
+
* reordering. Use `signedPath` to take it from a URL.
|
|
19
16
|
*/
|
|
20
17
|
path: string;
|
|
21
18
|
/**
|
|
@@ -48,7 +45,7 @@ export interface RequestProofInput {
|
|
|
48
45
|
*
|
|
49
46
|
* CallerProof = <account_proof bytes> || 0x00 (no session) || RequestSig
|
|
50
47
|
* RequestSig = u32le(len method) || method
|
|
51
|
-
* || u32le(len path) || path
|
|
48
|
+
* || u32le(len path) || path (with its query)
|
|
52
49
|
* || body_hash[32]
|
|
53
50
|
* || u64le(issued_at) || u64le(expires_at)
|
|
54
51
|
* || signature[64]
|
|
@@ -24,14 +24,8 @@
|
|
|
24
24
|
* this authenticates a caller without granting operator actions.
|
|
25
25
|
*/
|
|
26
26
|
import { concat, domainHash, fromHex, hex, importSigningKey, u32le, u64le, } from '../crypto/internal.js';
|
|
27
|
+
import { requestBodyHash, SIGN_DOMAIN } from '../request/request-sig.js';
|
|
27
28
|
const utf8 = (text) => new TextEncoder().encode(text);
|
|
28
|
-
/** `calimero.auth.request.v1` — what the request signature is separated by. */
|
|
29
|
-
const REQUEST_SIGN_DOMAIN = utf8('calimero.auth.request.v1');
|
|
30
|
-
/**
|
|
31
|
-
* `calimero.auth.request.body.v1` — distinct from the signing domain so a body
|
|
32
|
-
* hash can never be presented as a request signature preimage.
|
|
33
|
-
*/
|
|
34
|
-
const REQUEST_BODY_DOMAIN = utf8('calimero.auth.request.body.v1');
|
|
35
29
|
/** The header a signed request carries. */
|
|
36
30
|
export const REQUEST_PROOF_HEADER = 'X-Calimero-Proof';
|
|
37
31
|
/** Seconds a signature stays honourable. Short, because it bounds replay. */
|
|
@@ -47,7 +41,7 @@ const DEFAULT_VALID_FOR = 300;
|
|
|
47
41
|
*
|
|
48
42
|
* CallerProof = <account_proof bytes> || 0x00 (no session) || RequestSig
|
|
49
43
|
* RequestSig = u32le(len method) || method
|
|
50
|
-
* || u32le(len path) || path
|
|
44
|
+
* || u32le(len path) || path (with its query)
|
|
51
45
|
* || body_hash[32]
|
|
52
46
|
* || u64le(issued_at) || u64le(expires_at)
|
|
53
47
|
* || signature[64]
|
|
@@ -59,20 +53,10 @@ export async function signRequestProof(input) {
|
|
|
59
53
|
if (!path.startsWith('/')) {
|
|
60
54
|
throw new Error(`signRequestProof needs a path, got ${JSON.stringify(path)}`);
|
|
61
55
|
}
|
|
62
|
-
|
|
63
|
-
throw new Error(`the path must carry no query string, got ${JSON.stringify(path)} — ` +
|
|
64
|
-
'a query is excluded from the signature on purpose, because a proxy may ' +
|
|
65
|
-
'rewrite one. Anything that must be bound belongs in the body.');
|
|
66
|
-
}
|
|
67
|
-
const bodyBytes = input.body === undefined
|
|
68
|
-
? new Uint8Array()
|
|
69
|
-
: typeof input.body === 'string'
|
|
70
|
-
? utf8(input.body)
|
|
71
|
-
: input.body;
|
|
72
|
-
const bodyHash = await domainHash(REQUEST_BODY_DOMAIN, [bodyBytes]);
|
|
56
|
+
const bodyHash = await requestBodyHash(input.body);
|
|
73
57
|
const issuedAt = Math.floor(input.now ?? Date.now() / 1000);
|
|
74
58
|
const expiresAt = issuedAt + (input.validForSeconds ?? DEFAULT_VALID_FOR);
|
|
75
|
-
const payload = await domainHash(
|
|
59
|
+
const payload = await domainHash(SIGN_DOMAIN, [
|
|
76
60
|
utf8(method),
|
|
77
61
|
utf8(path),
|
|
78
62
|
bodyHash,
|