@calimero-network/mero-js 23.5.0 → 23.6.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/cloud/account-ownership.d.ts +9 -1
- package/dist/cloud/account-ownership.js +8 -2
- package/dist/cloud/cloud-client.d.ts +33 -1
- package/dist/cloud/cloud-client.js +43 -7
- package/dist/cloud/index.d.ts +1 -1
- package/dist/cloud/index.js +1 -1
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +3 -3
- package/dist/index.cjs +40 -6
- package/dist/index.cjs.map +2 -2
- package/dist/index.mjs +40 -6
- package/dist/index.mjs.map +2 -2
- package/package.json +1 -1
|
@@ -37,7 +37,15 @@ export interface AccountOwnershipClaimInput extends AccountClaimBase {
|
|
|
37
37
|
* Input for {@link signAccountHaClaim}: the same as the session claim, minus
|
|
38
38
|
* `subject` — there is no login to bind it to.
|
|
39
39
|
*/
|
|
40
|
-
export
|
|
40
|
+
export interface AccountHaClaimInput extends AccountClaimBase {
|
|
41
|
+
/**
|
|
42
|
+
* The relay that founded the namespace (the delegated session's `relayUrl`).
|
|
43
|
+
* Signed into the claim so nobody forwarding it can swap it: the cloud
|
|
44
|
+
* resolves it to one of its own fleet relays and hands that relay's
|
|
45
|
+
* addresses to the fleet node as its admitter. Leave it out when unknown.
|
|
46
|
+
*/
|
|
47
|
+
relayUrl?: string;
|
|
48
|
+
}
|
|
41
49
|
/** The audience of the session route's claim (`/api/cloud/me/...`). */
|
|
42
50
|
export declare const ACCOUNT_OWNERSHIP_AUDIENCE = "mdma:enable-ha-namespace";
|
|
43
51
|
/** The audience of the anonymous account route's claim (`/api/cloud/accounts/...`). */
|
|
@@ -19,6 +19,8 @@ const DOMAIN = new TextEncoder().encode('calimero.mdma.account-ownership-claim.v
|
|
|
19
19
|
/** What the cloud accepts; it caps a claim's lifetime at five minutes. */
|
|
20
20
|
const DEFAULT_TTL_MS = 60000;
|
|
21
21
|
const MAX_TTL_MS = 5 * 60000;
|
|
22
|
+
/** The cloud's limit on `relay_url`. */
|
|
23
|
+
const MAX_RELAY_URL = 1024;
|
|
22
24
|
/** The audience of the session route's claim (`/api/cloud/me/...`). */
|
|
23
25
|
export const ACCOUNT_OWNERSHIP_AUDIENCE = 'mdma:enable-ha-namespace';
|
|
24
26
|
/** The audience of the anonymous account route's claim (`/api/cloud/accounts/...`). */
|
|
@@ -39,7 +41,10 @@ const hex32 = (value, what) => {
|
|
|
39
41
|
* session claim keeps its exact field order and the account claim has no
|
|
40
42
|
* `subject` key at all (not a null one: the cloud tells the two apart by it).
|
|
41
43
|
*/
|
|
42
|
-
async function signClaim(input, audience, subject) {
|
|
44
|
+
async function signClaim(input, audience, subject, relayUrl) {
|
|
45
|
+
if (relayUrl !== undefined && !(relayUrl.length > 0 && relayUrl.length <= MAX_RELAY_URL)) {
|
|
46
|
+
throw new Error(`relayUrl must be a non-empty string of at most ${MAX_RELAY_URL} characters`);
|
|
47
|
+
}
|
|
43
48
|
const ttl = input.ttlMs ?? DEFAULT_TTL_MS;
|
|
44
49
|
if (!(ttl > 0 && ttl <= MAX_TTL_MS))
|
|
45
50
|
throw new Error(`ttlMs must be in (0, ${MAX_TTL_MS}]`);
|
|
@@ -59,6 +64,7 @@ async function signClaim(input, audience, subject) {
|
|
|
59
64
|
nonce,
|
|
60
65
|
issued_at_ms: issued,
|
|
61
66
|
expires_at_ms: issued + ttl,
|
|
67
|
+
...(relayUrl === undefined ? {} : { relay_url: relayUrl }),
|
|
62
68
|
}));
|
|
63
69
|
const signature = await signer.sign(concat(DOMAIN, payload));
|
|
64
70
|
return { kind: 'account', credential: input.credential, signed_payload: base64(payload), signature: base64(signature) };
|
|
@@ -77,5 +83,5 @@ export async function signAccountOwnershipClaim(input) {
|
|
|
77
83
|
* through the account's wallet link instead.
|
|
78
84
|
*/
|
|
79
85
|
export async function signAccountHaClaim(input) {
|
|
80
|
-
return signClaim(input, ACCOUNT_HA_AUDIENCE, undefined);
|
|
86
|
+
return signClaim(input, ACCOUNT_HA_AUDIENCE, undefined, input.relayUrl);
|
|
81
87
|
}
|
|
@@ -381,9 +381,15 @@ export interface EnableHaAsAccountOptions {
|
|
|
381
381
|
signer?: Signer;
|
|
382
382
|
/** The claim's lifetime in ms; default one minute, at most five. */
|
|
383
383
|
ttlMs?: number;
|
|
384
|
+
/**
|
|
385
|
+
* The relay that founded the namespace (the delegated session's `relayUrl`).
|
|
386
|
+
* The cloud gives that relay to the fleet node as its admitter; without it
|
|
387
|
+
* the node has to find the relay by discovery.
|
|
388
|
+
*/
|
|
389
|
+
relayUrl?: string;
|
|
384
390
|
}
|
|
385
391
|
/**
|
|
386
|
-
* The cloud's
|
|
392
|
+
* The cloud's refusals of {@link CloudClient.enableHaAsAccount} that a
|
|
387
393
|
* person can do something about. Both are `409`s with `{"error": code}`.
|
|
388
394
|
*
|
|
389
395
|
* Extends {@link HTTPError}, so `instanceof HTTPError` handling still catches it.
|
|
@@ -414,6 +420,32 @@ export declare class AccountLinkedToSeveralUsersError extends AccountHaRefusedEr
|
|
|
414
420
|
name: string;
|
|
415
421
|
static readonly CODE = "account_linked_to_several_users";
|
|
416
422
|
}
|
|
423
|
+
/**
|
|
424
|
+
* `ha_request_pending` (409): another namespace of this account is waiting for
|
|
425
|
+
* its first fleet node to be admitted. The route allows one at a time; it
|
|
426
|
+
* clears once that node is admitted, or by disabling HA on it from a cloud
|
|
427
|
+
* session. The claim was not spent.
|
|
428
|
+
*/
|
|
429
|
+
export declare class HaRequestPendingError extends AccountHaRefusedError {
|
|
430
|
+
name: string;
|
|
431
|
+
static readonly CODE = "ha_request_pending";
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* `unknown_relay` (422): the cloud runs no relay at `relayUrl`. The claim was
|
|
435
|
+
* not spent.
|
|
436
|
+
*/
|
|
437
|
+
export declare class UnknownRelayError extends AccountHaRefusedError {
|
|
438
|
+
name: string;
|
|
439
|
+
static readonly CODE = "unknown_relay";
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* `relay_not_dialable` (422): the relay is the cloud's but has not reported
|
|
443
|
+
* its addresses yet. The claim was not spent; retrying later can succeed.
|
|
444
|
+
*/
|
|
445
|
+
export declare class RelayNotDialableError extends AccountHaRefusedError {
|
|
446
|
+
name: string;
|
|
447
|
+
static readonly CODE = "relay_not_dialable";
|
|
448
|
+
}
|
|
417
449
|
export declare class CloudClient {
|
|
418
450
|
private baseUrl;
|
|
419
451
|
private session;
|
|
@@ -67,7 +67,7 @@ export function pickExecutingRelay(relays) {
|
|
|
67
67
|
return (relays.find((r) => r.canExecute && r.relayUrl !== null && r.executorAccount !== null) ?? null);
|
|
68
68
|
}
|
|
69
69
|
/**
|
|
70
|
-
* The cloud's
|
|
70
|
+
* The cloud's refusals of {@link CloudClient.enableHaAsAccount} that a
|
|
71
71
|
* person can do something about. Both are `409`s with `{"error": code}`.
|
|
72
72
|
*
|
|
73
73
|
* Extends {@link HTTPError}, so `instanceof HTTPError` handling still catches it.
|
|
@@ -105,6 +105,45 @@ export class AccountLinkedToSeveralUsersError extends AccountHaRefusedError {
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
AccountLinkedToSeveralUsersError.CODE = 'account_linked_to_several_users';
|
|
108
|
+
/**
|
|
109
|
+
* `ha_request_pending` (409): another namespace of this account is waiting for
|
|
110
|
+
* its first fleet node to be admitted. The route allows one at a time; it
|
|
111
|
+
* clears once that node is admitted, or by disabling HA on it from a cloud
|
|
112
|
+
* session. The claim was not spent.
|
|
113
|
+
*/
|
|
114
|
+
export class HaRequestPendingError extends AccountHaRefusedError {
|
|
115
|
+
constructor() {
|
|
116
|
+
super(...arguments);
|
|
117
|
+
this.name = 'HaRequestPendingError';
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
HaRequestPendingError.CODE = 'ha_request_pending';
|
|
121
|
+
/**
|
|
122
|
+
* `unknown_relay` (422): the cloud runs no relay at `relayUrl`. The claim was
|
|
123
|
+
* not spent.
|
|
124
|
+
*/
|
|
125
|
+
export class UnknownRelayError extends AccountHaRefusedError {
|
|
126
|
+
constructor() {
|
|
127
|
+
super(...arguments);
|
|
128
|
+
this.name = 'UnknownRelayError';
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
UnknownRelayError.CODE = 'unknown_relay';
|
|
132
|
+
/**
|
|
133
|
+
* `relay_not_dialable` (422): the relay is the cloud's but has not reported
|
|
134
|
+
* its addresses yet. The claim was not spent; retrying later can succeed.
|
|
135
|
+
*/
|
|
136
|
+
export class RelayNotDialableError extends AccountHaRefusedError {
|
|
137
|
+
constructor() {
|
|
138
|
+
super(...arguments);
|
|
139
|
+
this.name = 'RelayNotDialableError';
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
RelayNotDialableError.CODE = 'relay_not_dialable';
|
|
143
|
+
const ACCOUNT_HA_REFUSALS = {
|
|
144
|
+
409: [AccountNotLinkedError, AccountLinkedToSeveralUsersError, HaRequestPendingError],
|
|
145
|
+
422: [UnknownRelayError, RelayNotDialableError],
|
|
146
|
+
};
|
|
108
147
|
/**
|
|
109
148
|
* The `error` code of a 409 body. Accepts `{"error": code}` (the contract) and
|
|
110
149
|
* FastAPI's `{"detail": code}` / `{"detail": {"error": code}}`, so how the
|
|
@@ -598,6 +637,7 @@ export class CloudClient {
|
|
|
598
637
|
deviceSecret: options.deviceSecret,
|
|
599
638
|
signer: options.signer,
|
|
600
639
|
ttlMs: options.ttlMs,
|
|
640
|
+
relayUrl: options.relayUrl,
|
|
601
641
|
});
|
|
602
642
|
// The path carries the ids as the claim does — lowercase hex — so a caller
|
|
603
643
|
// that kept them uppercase does not address a different resource.
|
|
@@ -607,13 +647,9 @@ export class CloudClient {
|
|
|
607
647
|
return await this.request('POST', `/api/cloud/accounts/${encodeURIComponent(account)}/namespaces/${encodeURIComponent(namespace)}/enable-ha`, { body: { ownership_proof: proof }, anonymous: true });
|
|
608
648
|
}
|
|
609
649
|
catch (err) {
|
|
610
|
-
if (err instanceof HTTPError && err.status
|
|
650
|
+
if (err instanceof HTTPError && ACCOUNT_HA_REFUSALS[err.status]) {
|
|
611
651
|
const code = conflictCode(err.bodyText);
|
|
612
|
-
const Typed =
|
|
613
|
-
? AccountNotLinkedError
|
|
614
|
-
: code === AccountLinkedToSeveralUsersError.CODE
|
|
615
|
-
? AccountLinkedToSeveralUsersError
|
|
616
|
-
: undefined;
|
|
652
|
+
const Typed = ACCOUNT_HA_REFUSALS[err.status].find((t) => t.CODE === code);
|
|
617
653
|
if (Typed && code) {
|
|
618
654
|
throw new Typed(code, err.status, err.statusText, err.url, err.headers, err.bodyText);
|
|
619
655
|
}
|
package/dist/cloud/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { CloudClient, admitterAddrsFromNetworkStatus, AccountHaRefusedError, AccountNotLinkedError, AccountLinkedToSeveralUsersError, } from './cloud-client.js';
|
|
1
|
+
export { CloudClient, admitterAddrsFromNetworkStatus, AccountHaRefusedError, AccountNotLinkedError, AccountLinkedToSeveralUsersError, HaRequestPendingError, UnknownRelayError, RelayNotDialableError, } from './cloud-client.js';
|
|
2
2
|
export type { CloudClientConfig, CloudSession, CloudNamespace, CloudRelay, CloudAccountRelay, CloudNamespaceNode, CloudNamespaceRouting, CloudMachine, CloudMachineNamespace, CloudLinkedAccount, CloudLinkedAccounts, CloudAccountLink, AccountLinkChallenge, AccountLinkProof, AccountLinkHandoff, AccountLinkCallback, AccountLoginChallenge, AccountLoginProof, EnableHAOptions, DisableHAOptions, EnableHaAsAccountOptions, } from './cloud-client.js';
|
|
3
3
|
export { connectCloud, connectCloudWithAccount } from './connect.js';
|
|
4
4
|
export type { ConnectCloudOptions, ConnectCloudWithAccountOptions, CloudConnection, } from './connect.js';
|
package/dist/cloud/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { CloudClient, admitterAddrsFromNetworkStatus, AccountHaRefusedError, AccountNotLinkedError, AccountLinkedToSeveralUsersError, } from './cloud-client.js';
|
|
1
|
+
export { CloudClient, admitterAddrsFromNetworkStatus, AccountHaRefusedError, AccountNotLinkedError, AccountLinkedToSeveralUsersError, HaRequestPendingError, UnknownRelayError, RelayNotDialableError, } from './cloud-client.js';
|
|
2
2
|
export { connectCloud, connectCloudWithAccount } from './connect.js';
|
|
3
3
|
export { signRoutingChallenge, routingProofHeaders } from './routing-proof.js';
|
|
4
4
|
// Linking an account to a cloud login from a browser — the redirect half the
|