@calimero-network/mero-js 22.7.0 → 22.9.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.
Files changed (76) hide show
  1. package/dist/account/account.d.ts +99 -4
  2. package/dist/account/account.js +0 -0
  3. package/dist/account/index.d.ts +6 -2
  4. package/dist/account/index.js +3 -1
  5. package/dist/admin-api/admin-client.d.ts +154 -2
  6. package/dist/admin-api/admin-client.js +255 -1
  7. package/dist/admin-api/admin-types.d.ts +193 -5
  8. package/dist/cloud/cloud-client.d.ts +38 -8
  9. package/dist/cloud/cloud-client.js +38 -21
  10. package/dist/cloud/connect.d.ts +101 -2
  11. package/dist/cloud/connect.js +84 -8
  12. package/dist/cloud/enrol-redirect.d.ts +195 -0
  13. package/dist/cloud/enrol-redirect.js +236 -0
  14. package/dist/cloud/index.d.ts +6 -2
  15. package/dist/cloud/index.js +7 -1
  16. package/dist/cloud/link-redirect.d.ts +85 -0
  17. package/dist/cloud/link-redirect.js +105 -0
  18. package/dist/crypto/internal.d.ts +15 -0
  19. package/dist/crypto/internal.js +18 -0
  20. package/dist/device-cert/device-cert.d.ts +96 -2
  21. package/dist/device-cert/device-cert.js +101 -11
  22. package/dist/device-cert/device-scope.d.ts +58 -0
  23. package/dist/device-cert/device-scope.js +76 -0
  24. package/dist/device-cert/index.d.ts +4 -2
  25. package/dist/device-cert/index.js +2 -1
  26. package/dist/events/sse.d.ts +22 -2
  27. package/dist/events/sse.js +20 -11
  28. package/dist/http-client/http-types.d.ts +18 -0
  29. package/dist/http-client/web-client.js +21 -1
  30. package/dist/index.browser.mjs +2 -2
  31. package/dist/index.browser.mjs.map +4 -4
  32. package/dist/index.cjs +1955 -496
  33. package/dist/index.cjs.map +4 -4
  34. package/dist/index.d.ts +13 -4
  35. package/dist/index.js +21 -2
  36. package/dist/index.mjs +1955 -496
  37. package/dist/index.mjs.map +4 -4
  38. package/dist/invitation/index.d.ts +2 -0
  39. package/dist/invitation/index.js +2 -0
  40. package/dist/invitation/invitation.d.ts +106 -0
  41. package/dist/invitation/invitation.js +104 -0
  42. package/dist/login/login.d.ts +11 -1
  43. package/dist/login/login.js +5 -4
  44. package/dist/login/session.d.ts +21 -5
  45. package/dist/login/session.js +7 -15
  46. package/dist/namespace-op/namespace-op.d.ts +14 -2
  47. package/dist/namespace-op/namespace-op.js +13 -19
  48. package/dist/relay/index.d.ts +2 -2
  49. package/dist/relay/index.js +1 -1
  50. package/dist/relay/nonce-source.d.ts +135 -0
  51. package/dist/relay/nonce-source.js +187 -19
  52. package/dist/relay/relay-client.d.ts +60 -5
  53. package/dist/relay/relay-client.js +84 -4
  54. package/dist/relay-attestation/attest-node-key.d.ts +66 -0
  55. package/dist/relay-attestation/attest-node-key.js +110 -0
  56. package/dist/relay-attestation/index.d.ts +1 -0
  57. package/dist/relay-attestation/index.js +1 -0
  58. package/dist/request-proof/authorizer.d.ts +48 -0
  59. package/dist/request-proof/authorizer.js +65 -0
  60. package/dist/request-proof/index.d.ts +4 -0
  61. package/dist/request-proof/index.js +2 -0
  62. package/dist/request-proof/request-proof.d.ts +56 -0
  63. package/dist/request-proof/request-proof.js +111 -0
  64. package/dist/rpc/index.d.ts +21 -0
  65. package/dist/rpc/index.js +20 -0
  66. package/dist/transport/index.d.ts +6 -0
  67. package/dist/transport/index.js +3 -0
  68. package/dist/transport/mero-client.d.ts +286 -0
  69. package/dist/transport/mero-client.js +379 -0
  70. package/dist/transport/relay-observer.d.ts +152 -0
  71. package/dist/transport/relay-observer.js +173 -0
  72. package/dist/transport/relay-transport.d.ts +73 -0
  73. package/dist/transport/relay-transport.js +94 -0
  74. package/dist/transport/types.d.ts +100 -0
  75. package/dist/transport/types.js +32 -0
  76. package/package.json +1 -1
@@ -19,9 +19,22 @@ type SseEvent = SseEventData | GroupMembershipEventData | GroupMigrationEventDat
19
19
  type SseEventHandler = (event: SseEvent) => void;
20
20
  type SseConnectHandler = (sessionId: string) => void;
21
21
  type SseErrorHandler = (error: Error) => void;
22
+ /**
23
+ * How one request to the SSE endpoints is authenticated.
24
+ *
25
+ * A bearer token is one value reused across requests; a request proof commits to
26
+ * the method, the path and the body, so it must be produced per request. Both
27
+ * reduce to the same thing from this client's point of view — headers — so the
28
+ * stream takes the general form and {@link SseClient} adapts `getAuthToken` to it.
29
+ */
30
+ export type SseAuthorizer = (request: {
31
+ method: string;
32
+ path: string;
33
+ body?: string;
34
+ }) => Promise<Record<string, string>>;
22
35
  export declare class SseClient {
23
36
  private baseUrl;
24
- private getAuthToken;
37
+ private authorize;
25
38
  private reconnectDelayMs;
26
39
  private sessionId;
27
40
  private abortController;
@@ -33,7 +46,14 @@ export declare class SseClient {
33
46
  private fetchImpl;
34
47
  constructor(opts: {
35
48
  baseUrl: string;
36
- getAuthToken: () => Promise<string>;
49
+ /** A bearer token provider. The historical form, and still the default. */
50
+ getAuthToken?: () => Promise<string>;
51
+ /**
52
+ * Per-request authorization, for a credential that commits to the request —
53
+ * a device cert signing a request proof. Takes precedence over
54
+ * `getAuthToken`; one of the two is required.
55
+ */
56
+ authorize?: SseAuthorizer;
37
57
  reconnectDelayMs?: number;
38
58
  fetch?: typeof fetch;
39
59
  });
@@ -42,7 +42,16 @@ export class SseClient {
42
42
  this.closed = false;
43
43
  this.listeners = { connect: [], event: [], error: [] };
44
44
  this.baseUrl = opts.baseUrl.replace(/\/+$/, '');
45
- this.getAuthToken = opts.getAuthToken;
45
+ if (opts.authorize) {
46
+ this.authorize = opts.authorize;
47
+ }
48
+ else if (opts.getAuthToken) {
49
+ const getAuthToken = opts.getAuthToken;
50
+ this.authorize = async () => ({ Authorization: `Bearer ${await getAuthToken()}` });
51
+ }
52
+ else {
53
+ throw new Error('SseClient needs either `getAuthToken` or `authorize`');
54
+ }
46
55
  this.reconnectDelayMs = opts.reconnectDelayMs ?? 3000;
47
56
  // globalThis.fetch must be called as a method on globalThis, not through a
48
57
  // variable, or browsers throw "Illegal invocation".
@@ -123,11 +132,10 @@ export class SseClient {
123
132
  this.closed = false;
124
133
  this.abortController = new AbortController();
125
134
  try {
126
- const token = await this.getAuthToken();
127
135
  const url = `${this.baseUrl}/sse`;
128
136
  const response = await this.fetchImpl(url, {
129
137
  headers: {
130
- 'Authorization': `Bearer ${token}`,
138
+ ...(await this.authorize({ method: 'GET', path: new URL(url).pathname })),
131
139
  'Accept': 'text/event-stream',
132
140
  },
133
141
  signal: this.abortController.signal,
@@ -297,23 +305,24 @@ export class SseClient {
297
305
  }
298
306
  async sendSubscription(method, ids) {
299
307
  try {
300
- const token = await this.getAuthToken();
301
308
  const params = {};
302
309
  if (ids.contextIds && ids.contextIds.length > 0)
303
310
  params.contextIds = ids.contextIds;
304
311
  if (ids.groupIds && ids.groupIds.length > 0)
305
312
  params.groupIds = ids.groupIds;
306
- const response = await this.fetchImpl(`${this.baseUrl}/sse/subscription`, {
313
+ const url = `${this.baseUrl}/sse/subscription`;
314
+ // Serialized once and both signed and sent, because a proof commits to the
315
+ // body: re-stringifying for the signature risks two byte sequences that
316
+ // differ in key order, which fails as a refused credential rather than as
317
+ // anything that names the real cause.
318
+ const body = JSON.stringify({ id: this.sessionId, method, params });
319
+ const response = await this.fetchImpl(url, {
307
320
  method: 'POST',
308
321
  headers: {
309
- 'Authorization': `Bearer ${token}`,
322
+ ...(await this.authorize({ method: 'POST', path: new URL(url).pathname, body })),
310
323
  'Content-Type': 'application/json',
311
324
  },
312
- body: JSON.stringify({
313
- id: this.sessionId,
314
- method,
315
- params,
316
- }),
325
+ body,
317
326
  });
318
327
  if (!response.ok) {
319
328
  // The subscribe path used to report only the status. On the one failure
@@ -4,6 +4,24 @@ export interface Transport {
4
4
  baseUrl: string;
5
5
  defaultHeaders?: Record<string, string>;
6
6
  getAuthToken?: () => Promise<string | undefined>;
7
+ /**
8
+ * Per-request authorization, for a credential that commits to the request.
9
+ *
10
+ * `getAuthToken` returns one value reused across requests; a request proof
11
+ * signs the method, the path and the body, so it can only be produced where
12
+ * those are known. This is called with exactly that, and whatever it returns
13
+ * is merged into the headers.
14
+ *
15
+ * It runs AFTER `getAuthToken`, so a transport holding both sends both and
16
+ * lets the node decide — which it does by answering a request carrying a token
17
+ * by that token, and consulting a proof only when there is none.
18
+ */
19
+ authorizeRequest?: (request: {
20
+ method: string;
21
+ /** The path the node will see, query excluded. */
22
+ path: string;
23
+ body?: string | Uint8Array;
24
+ }) => Promise<Record<string, string>>;
7
25
  /**
8
26
  * Sign each request instead of, or alongside, presenting a session.
9
27
  *
@@ -193,8 +193,28 @@ export class WebHttpClient {
193
193
  else {
194
194
  headersObj = headers;
195
195
  }
196
+ const method = init?.method || 'GET';
197
+ // A credential that commits to the request can only be produced here, where
198
+ // the method, the assembled path and the body are all known. The path comes
199
+ // from the URL rather than from `path`, because a base URL may carry a prefix
200
+ // (`…/admin-api`) that the node sees and a bare relative path would not sign.
201
+ if (this.transport.authorizeRequest) {
202
+ const body = init?.body;
203
+ if (body !== undefined && typeof body !== 'string' && !(body instanceof Uint8Array)) {
204
+ throw new Error('this transport authorizes each request against its body, so the body must be a string ' +
205
+ 'or Uint8Array — a stream cannot be signed, because the signature would have to read ' +
206
+ 'bytes the request then cannot send.');
207
+ }
208
+ Object.assign(headersObj, await this.transport.authorizeRequest({
209
+ method,
210
+ path: new URL(url).pathname,
211
+ // Narrowed by the guard above; `BodyInit` is wider than what can be
212
+ // signed.
213
+ body: body,
214
+ }));
215
+ }
196
216
  const requestInit = {
197
- method: init?.method || 'GET',
217
+ method,
198
218
  headers: headersObj,
199
219
  };
200
220
  // Check if body is a stream (ReadableStream) that can't be reused