@calimero-network/mero-js 26.11.0 → 27.0.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/README.md +73 -288
- package/dist/cloud-v2/attestation.d.ts +91 -0
- package/dist/cloud-v2/attestation.js +135 -0
- package/dist/cloud-v2/capabilities.d.ts +30 -0
- package/dist/cloud-v2/capabilities.js +31 -0
- package/dist/cloud-v2/client.d.ts +426 -0
- package/dist/cloud-v2/client.js +646 -0
- package/dist/cloud-v2/errors.d.ts +98 -0
- package/dist/cloud-v2/errors.js +165 -0
- package/dist/cloud-v2/index.d.ts +10 -0
- package/dist/cloud-v2/index.js +5 -0
- package/dist/cloud-v2/passkey.d.ts +27 -0
- package/dist/cloud-v2/passkey.js +73 -0
- package/dist/crypto/internal.d.ts +3 -37
- package/dist/crypto/internal.js +3 -72
- package/dist/index.browser.mjs +4 -2
- package/dist/index.browser.mjs.map +4 -4
- package/dist/index.cjs +1750 -12772
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +3 -56
- package/dist/index.js +14 -75
- package/dist/index.mjs +1750 -12772
- package/dist/index.mjs.map +4 -4
- package/dist/sealed/index.d.ts +4 -4
- package/dist/sealed/index.js +2 -2
- package/dist/sealed/release.d.ts +21 -76
- package/dist/sealed/release.js +24 -108
- package/dist/sealed/sealed.d.ts +8 -56
- package/dist/sealed/sealed.js +11 -89
- package/dist/sealed/verify.d.ts +7 -13
- package/dist/sealed/verify.js +3 -7
- package/package.json +8 -13
- package/dist/account/account-admin.d.ts +0 -80
- package/dist/account/account-admin.js +0 -346
- package/dist/account/account.d.ts +0 -196
- package/dist/account/account.js +0 -269
- package/dist/account/application-id.d.ts +0 -50
- package/dist/account/application-id.js +0 -83
- package/dist/account/bootstrap-from-invitation.d.ts +0 -110
- package/dist/account/bootstrap-from-invitation.js +0 -80
- package/dist/account/create-context.d.ts +0 -103
- package/dist/account/create-context.js +0 -267
- package/dist/account/govern.d.ts +0 -24
- package/dist/account/govern.js +0 -50
- package/dist/account/index.d.ts +0 -26
- package/dist/account/index.js +0 -15
- package/dist/account/join-as-account.d.ts +0 -23
- package/dist/account/join-as-account.js +0 -34
- package/dist/account/join-with-node.d.ts +0 -115
- package/dist/account/join-with-node.js +0 -161
- package/dist/account/node-admin.d.ts +0 -20
- package/dist/account/node-admin.js +0 -33
- package/dist/account/relay-from-invitation.d.ts +0 -137
- package/dist/account/relay-from-invitation.js +0 -180
- package/dist/account/relay-login-error.d.ts +0 -32
- package/dist/account/relay-login-error.js +0 -34
- package/dist/account/session.d.ts +0 -266
- package/dist/account/session.js +0 -915
- package/dist/account/wordlist.d.ts +0 -16
- package/dist/account/wordlist.js +0 -273
- package/dist/admin-api/admin-client.d.ts +0 -688
- package/dist/admin-api/admin-client.js +0 -1230
- package/dist/admin-api/admin-factory.d.ts +0 -14
- package/dist/admin-api/admin-factory.js +0 -22
- package/dist/admin-api/admin-types.d.ts +0 -2046
- package/dist/admin-api/admin-types.js +0 -12
- package/dist/admin-api/index.d.ts +0 -4
- package/dist/admin-api/index.js +0 -5
- package/dist/admin-api/redeem-invitation.d.ts +0 -101
- package/dist/admin-api/redeem-invitation.js +0 -200
- package/dist/auth/index.d.ts +0 -34
- package/dist/auth/index.js +0 -67
- package/dist/auth-api/auth-client.d.ts +0 -31
- package/dist/auth-api/auth-client.js +0 -117
- package/dist/auth-api/auth-factory.d.ts +0 -7
- package/dist/auth-api/auth-factory.js +0 -41
- package/dist/auth-api/auth-types.d.ts +0 -130
- package/dist/auth-api/auth-types.js +0 -2
- package/dist/auth-api/index.d.ts +0 -3
- package/dist/auth-api/index.js +0 -4
- package/dist/capabilities.d.ts +0 -84
- package/dist/capabilities.js +0 -117
- package/dist/cloud/account-ownership.d.ts +0 -63
- package/dist/cloud/account-ownership.js +0 -87
- package/dist/cloud/cloud-client.d.ts +0 -949
- package/dist/cloud/cloud-client.js +0 -1068
- package/dist/cloud/connect.d.ts +0 -213
- package/dist/cloud/connect.js +0 -260
- package/dist/cloud/enrol-redirect.d.ts +0 -181
- package/dist/cloud/enrol-redirect.js +0 -244
- package/dist/cloud/index.d.ts +0 -12
- package/dist/cloud/index.js +0 -10
- package/dist/cloud/link-redirect.d.ts +0 -95
- package/dist/cloud/link-redirect.js +0 -121
- package/dist/cloud/link-state.d.ts +0 -36
- package/dist/cloud/link-state.js +0 -104
- package/dist/cloud/routing-proof.d.ts +0 -113
- package/dist/cloud/routing-proof.js +0 -96
- package/dist/device-cert/device-cert.d.ts +0 -189
- package/dist/device-cert/device-cert.js +0 -239
- package/dist/device-cert/device-scope.d.ts +0 -58
- package/dist/device-cert/device-scope.js +0 -76
- package/dist/device-cert/index.d.ts +0 -4
- package/dist/device-cert/index.js +0 -3
- package/dist/ephemeral/codec.d.ts +0 -4
- package/dist/ephemeral/codec.js +0 -12
- package/dist/ephemeral/index.d.ts +0 -57
- package/dist/ephemeral/index.js +0 -101
- package/dist/ephemeral/types.d.ts +0 -27
- package/dist/ephemeral/types.js +0 -1
- package/dist/errors/classify.d.ts +0 -65
- package/dist/errors/classify.js +0 -117
- package/dist/errors/index.d.ts +0 -2
- package/dist/errors/index.js +0 -1
- package/dist/events/group.d.ts +0 -95
- package/dist/events/group.js +0 -10
- package/dist/events/index.d.ts +0 -5
- package/dist/events/index.js +0 -2
- package/dist/events/sse.d.ts +0 -127
- package/dist/events/sse.js +0 -469
- package/dist/events/ws.d.ts +0 -57
- package/dist/events/ws.js +0 -212
- package/dist/http-client/api-response.d.ts +0 -15
- package/dist/http-client/api-response.js +0 -1
- package/dist/http-client/http-factory.d.ts +0 -71
- package/dist/http-client/http-factory.js +0 -68
- package/dist/http-client/http-types.d.ts +0 -87
- package/dist/http-client/http-types.js +0 -1
- package/dist/http-client/index.d.ts +0 -7
- package/dist/http-client/index.js +0 -11
- package/dist/http-client/path-segment.d.ts +0 -1
- package/dist/http-client/path-segment.js +0 -8
- package/dist/http-client/retry.d.ts +0 -5
- package/dist/http-client/retry.js +0 -82
- package/dist/http-client/signal-utils.d.ts +0 -2
- package/dist/http-client/signal-utils.js +0 -41
- package/dist/http-client/web-client.d.ts +0 -80
- package/dist/http-client/web-client.js +0 -557
- package/dist/invitation/index.d.ts +0 -2
- package/dist/invitation/index.js +0 -2
- package/dist/invitation/invitation.d.ts +0 -106
- package/dist/invitation/invitation.js +0 -104
- package/dist/login/index.d.ts +0 -4
- package/dist/login/index.js +0 -3
- package/dist/login/login.d.ts +0 -111
- package/dist/login/login.js +0 -110
- package/dist/login/session.d.ts +0 -124
- package/dist/login/session.js +0 -141
- package/dist/mero-js.d.ts +0 -231
- package/dist/mero-js.js +0 -469
- package/dist/namespace-op/index.d.ts +0 -2
- package/dist/namespace-op/index.js +0 -3
- package/dist/namespace-op/namespace-op.d.ts +0 -150
- package/dist/namespace-op/namespace-op.js +0 -191
- package/dist/nodeDiscovery.d.ts +0 -47
- package/dist/nodeDiscovery.js +0 -101
- package/dist/owner-op/index.d.ts +0 -2
- package/dist/owner-op/index.js +0 -3
- package/dist/owner-op/owner-op.d.ts +0 -119
- package/dist/owner-op/owner-op.js +0 -214
- package/dist/presence/relay-presence.d.ts +0 -56
- package/dist/presence/relay-presence.js +0 -107
- package/dist/presence/statement.d.ts +0 -12
- package/dist/presence/statement.js +0 -17
- package/dist/relay/index.d.ts +0 -4
- package/dist/relay/index.js +0 -2
- package/dist/relay/nonce-source.d.ts +0 -72
- package/dist/relay/nonce-source.js +0 -111
- package/dist/relay/relay-client.d.ts +0 -472
- package/dist/relay/relay-client.js +0 -518
- package/dist/relay-attestation/attest-node-key.d.ts +0 -66
- package/dist/relay-attestation/attest-node-key.js +0 -110
- package/dist/relay-attestation/index.d.ts +0 -1
- package/dist/relay-attestation/index.js +0 -1
- package/dist/request/caller-proof.d.ts +0 -43
- package/dist/request/caller-proof.js +0 -36
- package/dist/request/index.d.ts +0 -3
- package/dist/request/index.js +0 -3
- package/dist/request/proof-signer.d.ts +0 -49
- package/dist/request/proof-signer.js +0 -59
- package/dist/request/request-sig.d.ts +0 -99
- package/dist/request/request-sig.js +0 -95
- package/dist/request-proof/authorizer.d.ts +0 -41
- package/dist/request-proof/authorizer.js +0 -60
- package/dist/request-proof/index.d.ts +0 -4
- package/dist/request-proof/index.js +0 -2
- package/dist/request-proof/request-proof.d.ts +0 -53
- package/dist/request-proof/request-proof.js +0 -95
- package/dist/rpc/index.d.ts +0 -64
- package/dist/rpc/index.js +0 -78
- package/dist/signer/index.d.ts +0 -2
- package/dist/signer/index.js +0 -3
- package/dist/signer/signer.d.ts +0 -92
- package/dist/signer/signer.js +0 -147
- package/dist/token-store/index.d.ts +0 -27
- package/dist/token-store/index.js +0 -62
- package/dist/transport/index.d.ts +0 -6
- package/dist/transport/index.js +0 -3
- package/dist/transport/mero-client.d.ts +0 -294
- package/dist/transport/mero-client.js +0 -386
- package/dist/transport/relay-observer.d.ts +0 -152
- package/dist/transport/relay-observer.js +0 -173
- package/dist/transport/relay-transport.d.ts +0 -73
- package/dist/transport/relay-transport.js +0 -94
- package/dist/transport/types.d.ts +0 -100
- package/dist/transport/types.js +0 -32
- package/dist/types/api-response.d.ts +0 -15
- package/dist/types/api-response.js +0 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/index.js +0 -1
- package/dist/warrant/creation-warrant.d.ts +0 -163
- package/dist/warrant/creation-warrant.js +0 -194
- package/dist/warrant/governance-op.d.ts +0 -202
- package/dist/warrant/governance-op.js +0 -355
- package/dist/warrant/governance-warrant.d.ts +0 -124
- package/dist/warrant/governance-warrant.js +0 -164
- package/dist/warrant/index.d.ts +0 -8
- package/dist/warrant/index.js +0 -5
- package/dist/warrant/warrant.d.ts +0 -188
- package/dist/warrant/warrant.js +0 -202
package/dist/cloud/connect.d.ts
DELETED
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The second way to log in: sign in to Calimero Cloud instead of connecting to
|
|
3
|
-
* a node.
|
|
4
|
-
*
|
|
5
|
-
* # Two connection shapes, one SDK
|
|
6
|
-
*
|
|
7
|
-
* `createMeroJs({ baseUrl })` is the node connection: point at a node, hold a
|
|
8
|
-
* credential on it, and drive its admin API and JSON-RPC. It assumes the caller
|
|
9
|
-
* has a node — their own, or one they have an account on.
|
|
10
|
-
*
|
|
11
|
-
* `connectCloud(...)` is for the caller who has neither. They sign in with the
|
|
12
|
-
* identity they already use (Google, through the cloud), and the cloud answers
|
|
13
|
-
* with an attested TEE relay serving their namespace. Writes then go through
|
|
14
|
-
* delegated execution: signed here, run there, attributed here.
|
|
15
|
-
*
|
|
16
|
-
* The two are not alternatives to be picked at random — they answer different
|
|
17
|
-
* questions. "Which node do I talk to?" has an answer for a desktop app and no
|
|
18
|
-
* answer at all for a web app whose user has never installed anything. This
|
|
19
|
-
* function is that missing answer, which is why the login step it replaces is
|
|
20
|
-
* the *node URL prompt* rather than the authentication.
|
|
21
|
-
*
|
|
22
|
-
* `connectCloudWithAccount(...)` is the same connection reached by the other
|
|
23
|
-
* door: the caller holds an account root and signs in as the account itself,
|
|
24
|
-
* with no Google login anywhere in it. Identical from there on, deliberately —
|
|
25
|
-
* which login proved who you are changes nothing about how a relay is found or
|
|
26
|
-
* how a write is authorised.
|
|
27
|
-
*
|
|
28
|
-
* # What it does not do
|
|
29
|
-
*
|
|
30
|
-
* # Sealing the relay
|
|
31
|
-
*
|
|
32
|
-
* TLS in front of a relay ends outside its TD, at the host's ingress, where
|
|
33
|
-
* the warrant and the method's arguments are readable. Pass `seal` (a quote
|
|
34
|
-
* verifier) and every call to the relay is sealed to the key its attested TD
|
|
35
|
-
* holds, so they are readable only inside it.
|
|
36
|
-
*
|
|
37
|
-
* It does not read state. A relay writes on the author's behalf; reads still
|
|
38
|
-
* come from a node the client can query, or from the app's own projection of
|
|
39
|
-
* the events it receives. `connectCloud` is deliberately narrow about this
|
|
40
|
-
* rather than presenting a half-working `MeroJs` whose `admin` and `rpc` would
|
|
41
|
-
* need a credential nobody holds.
|
|
42
|
-
*/
|
|
43
|
-
import { CloudClient } from './cloud-client.js';
|
|
44
|
-
import type { CloudClientConfig, CloudNamespace, CloudRelay } from './cloud-client.js';
|
|
45
|
-
import { RelayClient } from '../relay/relay-client.js';
|
|
46
|
-
import type { IntentResult, NonceSource } from '../relay/index.js';
|
|
47
|
-
import { type Signer } from '../signer/signer.js';
|
|
48
|
-
import { type RootSource } from '../account/account.js';
|
|
49
|
-
import { MeroClient } from '../transport/mero-client.js';
|
|
50
|
-
import type { Audience } from '../login/index.js';
|
|
51
|
-
import type { VerifyTransportQuote } from '../sealed/sealed.js';
|
|
52
|
-
export interface ConnectCloudOptions {
|
|
53
|
-
/** Defaults to the hosted cloud. */
|
|
54
|
-
cloudBaseUrl?: string;
|
|
55
|
-
/** A stored MDMA session token. Provide this or `googleIdToken`. */
|
|
56
|
-
sessionToken?: string;
|
|
57
|
-
/** A Google ID token to exchange for a session. Provide this or `sessionToken`. */
|
|
58
|
-
googleIdToken?: string;
|
|
59
|
-
/** Persist a new or rolling-refreshed session. See {@link CloudClientConfig.onSession}. */
|
|
60
|
-
onSession?: CloudClientConfig['onSession'];
|
|
61
|
-
/** The author's account, hex — whose writes these will be. */
|
|
62
|
-
authorAccount: string;
|
|
63
|
-
/** The author's `AccountProof<DeviceCert>`, hex-encoded borsh. */
|
|
64
|
-
authorProof: string;
|
|
65
|
-
/**
|
|
66
|
-
* The author device's ed25519 signing secret, hex. Never transmitted.
|
|
67
|
-
*
|
|
68
|
-
* Mutually exclusive with {@link ConnectCloudOptions.signer}, and the weaker
|
|
69
|
-
* of the two in a browser — a secret that exists as a string can be read by
|
|
70
|
-
* anything on the origin.
|
|
71
|
-
*/
|
|
72
|
-
deviceSecret?: string;
|
|
73
|
-
/**
|
|
74
|
-
* The author device's signer — use instead of
|
|
75
|
-
* {@link ConnectCloudOptions.deviceSecret}.
|
|
76
|
-
*
|
|
77
|
-
* A browser wallet holds its device key as a non-extractable `CryptoKey`,
|
|
78
|
-
* which has no hex form to pass. Without this, that wallet — the case this
|
|
79
|
-
* whole path was built for — had to hand-assemble the `RelayClient` the
|
|
80
|
-
* one-call path exists to build.
|
|
81
|
-
*/
|
|
82
|
-
signer?: Signer;
|
|
83
|
-
/**
|
|
84
|
-
* Which namespace to write in.
|
|
85
|
-
*
|
|
86
|
-
* Optional only when the user owns exactly one: guessing among several would
|
|
87
|
-
* pick a tenant for them, and the failure would be a write landing in the
|
|
88
|
-
* wrong place rather than an error.
|
|
89
|
-
*/
|
|
90
|
-
namespaceId?: string;
|
|
91
|
-
/**
|
|
92
|
-
* Where nonces come from.
|
|
93
|
-
*
|
|
94
|
-
* Defaults to `localStorage`, keyed by the author's device public key, so a
|
|
95
|
-
* reload continues the sequence instead of replaying it. Pass
|
|
96
|
-
* {@link createMemoryNonceSource} explicitly for a process that owns its
|
|
97
|
-
* whole sequence, or your own source to coordinate across tabs.
|
|
98
|
-
*/
|
|
99
|
-
nonces?: NonceSource;
|
|
100
|
-
/** Seconds a minted warrant stays presentable. Defaults to 300. */
|
|
101
|
-
ttlSeconds?: number;
|
|
102
|
-
/**
|
|
103
|
-
* Seal every call to the relay to its attested TEE, verified with this — for
|
|
104
|
-
* example {@link createQuoteVerifier} with the MRTDs you trust. The warrant
|
|
105
|
-
* and the method's arguments then reach only the relay's TD, not whatever
|
|
106
|
-
* terminates TLS in front of it, and the relay is one this client checked
|
|
107
|
-
* itself rather than one the cloud vouched for. Calls to the cloud are not
|
|
108
|
-
* sealed; they carry no intent.
|
|
109
|
-
*
|
|
110
|
-
* Without it, calls to the relay are plain HTTPS.
|
|
111
|
-
*/
|
|
112
|
-
seal?: VerifyTransportQuote;
|
|
113
|
-
fetch?: typeof fetch;
|
|
114
|
-
timeoutMs?: number;
|
|
115
|
-
/**
|
|
116
|
-
* The chosen relay node's device signing key, hex — if you know it.
|
|
117
|
-
*
|
|
118
|
-
* Supplying it makes `connection.client` able to *observe* as well as write:
|
|
119
|
-
* a relay is an ordinary node, and a keyholder with an account proof can log
|
|
120
|
-
* in to it and subscribe. The key has to come from somewhere the caller
|
|
121
|
-
* already trusts, which today means out of band, because the cloud does not
|
|
122
|
-
* publish it (mdma #312).
|
|
123
|
-
*
|
|
124
|
-
* Left unset, the key the cloud reported is used — `null` for now, which
|
|
125
|
-
* means `connection.client.canSubscribe === false` and nothing is guessed.
|
|
126
|
-
* When #312 lands, unchanged callers start observing.
|
|
127
|
-
*/
|
|
128
|
-
relayNodeKey?: string;
|
|
129
|
-
/**
|
|
130
|
-
* The audience the observing session is bound to. Defaults to this runtime's
|
|
131
|
-
* own origin (or `cli`). See {@link RelayObserveConfig.audience}.
|
|
132
|
-
*/
|
|
133
|
-
observeAudience?: Audience;
|
|
134
|
-
}
|
|
135
|
-
/** A signed-in cloud session with a relay ready to write through. */
|
|
136
|
-
export interface CloudConnection {
|
|
137
|
-
/** The cloud API, for namespaces, billing, HA and sign-out. */
|
|
138
|
-
cloud: CloudClient;
|
|
139
|
-
/** The relay chosen for this namespace. */
|
|
140
|
-
relay: RelayClient;
|
|
141
|
-
/** The namespace writes will land in. */
|
|
142
|
-
namespaceId: string;
|
|
143
|
-
namespace: CloudNamespace;
|
|
144
|
-
/** What the cloud reported about the chosen relay. */
|
|
145
|
-
relayInfo: CloudRelay;
|
|
146
|
-
/** Run one method in one context, on the author's behalf. */
|
|
147
|
-
execute<T = unknown>(contextId: string, method: string, argsJson?: unknown): Promise<IntentResult<T>>;
|
|
148
|
-
/**
|
|
149
|
-
* The same connection behind the transport-neutral surface.
|
|
150
|
-
*
|
|
151
|
-
* `client.rpc.execute({ contextId, method, argsJson })` is the call a
|
|
152
|
-
* node-backed app already writes, so an app reaching the cloud this way keeps
|
|
153
|
-
* every call site it has — signing in *is* the transport decision, and it
|
|
154
|
-
* happens here, once. `relay` and `execute` above are unchanged and remain
|
|
155
|
-
* the direct path for a caller that wants `IntentResult` in hand.
|
|
156
|
-
*
|
|
157
|
-
* Whether it can also *observe* depends on one input: the relay node's
|
|
158
|
-
* device signing key. A relay is an ordinary node and serves `/sse` and
|
|
159
|
-
* `/ws`, but a login statement must be signed against a key learned out of
|
|
160
|
-
* band. Pass {@link ConnectCloudOptions.relayNodeKey} if you have it; the
|
|
161
|
-
* cloud does not publish it yet (mdma #312), so by default
|
|
162
|
-
* `client.canSubscribe === false` and `client.events` throws saying so.
|
|
163
|
-
*/
|
|
164
|
-
client: MeroClient;
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Sign in to the cloud and return a connection that can write.
|
|
168
|
-
*
|
|
169
|
-
* Throws rather than returning a half-usable connection, and the message says
|
|
170
|
-
* which step is missing — no namespace, several to choose between, HA not
|
|
171
|
-
* enabled, or no relay that relays (only replicas, or relays waiting for a
|
|
172
|
-
* grant). Every one of those is
|
|
173
|
-
* something the user has to go and do, and a client that reports them alike
|
|
174
|
-
* cannot tell them what.
|
|
175
|
-
*/
|
|
176
|
-
export declare function connectCloud(options: ConnectCloudOptions): Promise<CloudConnection>;
|
|
177
|
-
/** What {@link connectCloudWithAccount} needs on top of a root. */
|
|
178
|
-
export interface ConnectCloudWithAccountOptions extends Omit<ConnectCloudOptions, 'sessionToken' | 'googleIdToken' | 'authorAccount'> {
|
|
179
|
-
/**
|
|
180
|
-
* The account root — a 64-hex secret, or a `Signer` over a key that cannot be
|
|
181
|
-
* exported. The whole credential: no Google login is involved.
|
|
182
|
-
*/
|
|
183
|
-
root: RootSource;
|
|
184
|
-
/**
|
|
185
|
-
* Whose writes these are, hex. Defaults to the account the root derives,
|
|
186
|
-
* which is the only account it can open a session as.
|
|
187
|
-
*
|
|
188
|
-
* Worth passing only when the device certificate was issued by a *different*
|
|
189
|
-
* root than the one signing in — which core accepts and nothing here needs to
|
|
190
|
-
* forbid, but which is rare enough that defaulting to the signing root is the
|
|
191
|
-
* safer shape.
|
|
192
|
-
*/
|
|
193
|
-
authorAccount?: string;
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* The same connection, opened with an account root instead of a cloud session.
|
|
197
|
-
*
|
|
198
|
-
* The two sign-in paths end in the same place, and until now only one of them
|
|
199
|
-
* ended in a usable client: a caller signing in with a root had to open the
|
|
200
|
-
* session, read the relays and assemble a {@link RelayClient} by hand, which is
|
|
201
|
-
* three chances to pair the wrong author with the wrong executor.
|
|
202
|
-
*
|
|
203
|
-
* It is one call rather than one function: the session is minted here and
|
|
204
|
-
* everything after it is {@link connectCloud}, unchanged. Duplicating the
|
|
205
|
-
* namespace choice and the three no-relay diagnostics would mean two copies of
|
|
206
|
-
* the messages that tell a user what to go and do.
|
|
207
|
-
*
|
|
208
|
-
* The author defaults to the account the root derives. That is not a
|
|
209
|
-
* convenience — passing an account the root does not own is the mistake this
|
|
210
|
-
* shape removes, since the cloud would answer with that root's namespaces while
|
|
211
|
-
* every warrant claimed somebody else's authorship.
|
|
212
|
-
*/
|
|
213
|
-
export declare function connectCloudWithAccount(options: ConnectCloudWithAccountOptions): Promise<CloudConnection>;
|
package/dist/cloud/connect.js
DELETED
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The second way to log in: sign in to Calimero Cloud instead of connecting to
|
|
3
|
-
* a node.
|
|
4
|
-
*
|
|
5
|
-
* # Two connection shapes, one SDK
|
|
6
|
-
*
|
|
7
|
-
* `createMeroJs({ baseUrl })` is the node connection: point at a node, hold a
|
|
8
|
-
* credential on it, and drive its admin API and JSON-RPC. It assumes the caller
|
|
9
|
-
* has a node — their own, or one they have an account on.
|
|
10
|
-
*
|
|
11
|
-
* `connectCloud(...)` is for the caller who has neither. They sign in with the
|
|
12
|
-
* identity they already use (Google, through the cloud), and the cloud answers
|
|
13
|
-
* with an attested TEE relay serving their namespace. Writes then go through
|
|
14
|
-
* delegated execution: signed here, run there, attributed here.
|
|
15
|
-
*
|
|
16
|
-
* The two are not alternatives to be picked at random — they answer different
|
|
17
|
-
* questions. "Which node do I talk to?" has an answer for a desktop app and no
|
|
18
|
-
* answer at all for a web app whose user has never installed anything. This
|
|
19
|
-
* function is that missing answer, which is why the login step it replaces is
|
|
20
|
-
* the *node URL prompt* rather than the authentication.
|
|
21
|
-
*
|
|
22
|
-
* `connectCloudWithAccount(...)` is the same connection reached by the other
|
|
23
|
-
* door: the caller holds an account root and signs in as the account itself,
|
|
24
|
-
* with no Google login anywhere in it. Identical from there on, deliberately —
|
|
25
|
-
* which login proved who you are changes nothing about how a relay is found or
|
|
26
|
-
* how a write is authorised.
|
|
27
|
-
*
|
|
28
|
-
* # What it does not do
|
|
29
|
-
*
|
|
30
|
-
* # Sealing the relay
|
|
31
|
-
*
|
|
32
|
-
* TLS in front of a relay ends outside its TD, at the host's ingress, where
|
|
33
|
-
* the warrant and the method's arguments are readable. Pass `seal` (a quote
|
|
34
|
-
* verifier) and every call to the relay is sealed to the key its attested TD
|
|
35
|
-
* holds, so they are readable only inside it.
|
|
36
|
-
*
|
|
37
|
-
* It does not read state. A relay writes on the author's behalf; reads still
|
|
38
|
-
* come from a node the client can query, or from the app's own projection of
|
|
39
|
-
* the events it receives. `connectCloud` is deliberately narrow about this
|
|
40
|
-
* rather than presenting a half-working `MeroJs` whose `admin` and `rpc` would
|
|
41
|
-
* need a credential nobody holds.
|
|
42
|
-
*/
|
|
43
|
-
import { CloudClient, pickExecutingRelay } from './cloud-client.js';
|
|
44
|
-
import { RelayClient } from '../relay/relay-client.js';
|
|
45
|
-
import { createLocalStorageNonceSource, createMemoryNonceSource } from '../relay/nonce-source.js';
|
|
46
|
-
import { resolveSigner } from '../signer/signer.js';
|
|
47
|
-
import { resolveRoot } from '../account/account.js';
|
|
48
|
-
import { MeroClient } from '../transport/mero-client.js';
|
|
49
|
-
import { createAttestedSealedFetch } from '../sealed/sealed.js';
|
|
50
|
-
/**
|
|
51
|
-
* Sign in to the cloud and return a connection that can write.
|
|
52
|
-
*
|
|
53
|
-
* Throws rather than returning a half-usable connection, and the message says
|
|
54
|
-
* which step is missing — no namespace, several to choose between, HA not
|
|
55
|
-
* enabled, or no relay that relays (only replicas, or relays waiting for a
|
|
56
|
-
* grant). Every one of those is
|
|
57
|
-
* something the user has to go and do, and a client that reports them alike
|
|
58
|
-
* cannot tell them what.
|
|
59
|
-
*/
|
|
60
|
-
export async function connectCloud(options) {
|
|
61
|
-
if (!options.sessionToken && !options.googleIdToken) {
|
|
62
|
-
throw new Error('connectCloud needs either a sessionToken or a googleIdToken');
|
|
63
|
-
}
|
|
64
|
-
// Resolved before the first network call: a missing or duplicated key is the
|
|
65
|
-
// caller's mistake to fix, and finding out after signing in leaves a session
|
|
66
|
-
// minted for a connection that was never going to be built.
|
|
67
|
-
const signer = await resolveSigner(options.deviceSecret, options.signer, 'deviceSecret');
|
|
68
|
-
const cloud = new CloudClient({
|
|
69
|
-
cloudBaseUrl: options.cloudBaseUrl,
|
|
70
|
-
sessionToken: options.sessionToken,
|
|
71
|
-
onSession: options.onSession,
|
|
72
|
-
fetch: options.fetch,
|
|
73
|
-
timeoutMs: options.timeoutMs,
|
|
74
|
-
});
|
|
75
|
-
if (!options.sessionToken && options.googleIdToken) {
|
|
76
|
-
await cloud.signInWithGoogle(options.googleIdToken);
|
|
77
|
-
}
|
|
78
|
-
const namespaces = await cloud.getMyNamespaces();
|
|
79
|
-
const namespace = pickNamespace(namespaces, options.namespaceId);
|
|
80
|
-
// One read, used for both the choice and the diagnostic. `findExecutingRelay`
|
|
81
|
-
// would fetch again to explain itself, and a second call can disagree with the
|
|
82
|
-
// first — reporting "waiting for the grant" about a relay that was already
|
|
83
|
-
// usable a moment ago.
|
|
84
|
-
const relays = await cloud.getNamespaceRelays(namespace.namespaceId);
|
|
85
|
-
const relayInfo = pickExecutingRelay(relays);
|
|
86
|
-
if (!relayInfo) {
|
|
87
|
-
throw new Error(explainNoRelay(namespace, relays));
|
|
88
|
-
}
|
|
89
|
-
const relay = new RelayClient({
|
|
90
|
-
// Checked by `pickExecutingRelay`, which is the only thing that returns a
|
|
91
|
-
// relay — narrowed here rather than re-validated, so the invariant lives in
|
|
92
|
-
// one place.
|
|
93
|
-
relayUrl: relayInfo.relayUrl,
|
|
94
|
-
executorAccount: relayInfo.executorAccount,
|
|
95
|
-
authorAccount: options.authorAccount,
|
|
96
|
-
authorProof: options.authorProof,
|
|
97
|
-
signer,
|
|
98
|
-
nonces: options.nonces ?? defaultNonceSource(signer.publicKey),
|
|
99
|
-
ttlSeconds: options.ttlSeconds,
|
|
100
|
-
// Only the relay is sealed: a sealed fetch is bound to one node and refuses
|
|
101
|
-
// any other URL, and the cloud calls above carry no intent.
|
|
102
|
-
fetch: options.seal
|
|
103
|
-
? createAttestedSealedFetch({
|
|
104
|
-
baseUrl: relayInfo.relayUrl,
|
|
105
|
-
verify: options.seal,
|
|
106
|
-
fetch: options.fetch,
|
|
107
|
-
})
|
|
108
|
-
: options.fetch,
|
|
109
|
-
timeoutMs: options.timeoutMs,
|
|
110
|
-
});
|
|
111
|
-
return {
|
|
112
|
-
cloud,
|
|
113
|
-
relay,
|
|
114
|
-
namespaceId: namespace.namespaceId,
|
|
115
|
-
namespace,
|
|
116
|
-
relayInfo,
|
|
117
|
-
execute: (contextId, method, argsJson) => relay.execute(contextId, method, argsJson),
|
|
118
|
-
// Built from the same `relay`, not a second one: one nonce sequence, one
|
|
119
|
-
// warrant signer. Two clients over one relay would hand out the same nonce
|
|
120
|
-
// twice and have the network refuse the loser as a replay.
|
|
121
|
-
//
|
|
122
|
-
// `observe` is always passed, with whatever key is known — the caller's if
|
|
123
|
-
// they had one, otherwise the cloud's, which is `null` until mdma #312. One
|
|
124
|
-
// code path rather than two, so the hosted case starts observing the day
|
|
125
|
-
// the field appears, without a line changing here or at the call site.
|
|
126
|
-
client: new MeroClient({
|
|
127
|
-
transport: 'relay',
|
|
128
|
-
relay,
|
|
129
|
-
observe: {
|
|
130
|
-
nodeKey: options.relayNodeKey ?? relayInfo.nodeKey,
|
|
131
|
-
audience: options.observeAudience,
|
|
132
|
-
fetch: options.fetch,
|
|
133
|
-
timeoutMs: options.timeoutMs,
|
|
134
|
-
},
|
|
135
|
-
}),
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* The same connection, opened with an account root instead of a cloud session.
|
|
140
|
-
*
|
|
141
|
-
* The two sign-in paths end in the same place, and until now only one of them
|
|
142
|
-
* ended in a usable client: a caller signing in with a root had to open the
|
|
143
|
-
* session, read the relays and assemble a {@link RelayClient} by hand, which is
|
|
144
|
-
* three chances to pair the wrong author with the wrong executor.
|
|
145
|
-
*
|
|
146
|
-
* It is one call rather than one function: the session is minted here and
|
|
147
|
-
* everything after it is {@link connectCloud}, unchanged. Duplicating the
|
|
148
|
-
* namespace choice and the three no-relay diagnostics would mean two copies of
|
|
149
|
-
* the messages that tell a user what to go and do.
|
|
150
|
-
*
|
|
151
|
-
* The author defaults to the account the root derives. That is not a
|
|
152
|
-
* convenience — passing an account the root does not own is the mistake this
|
|
153
|
-
* shape removes, since the cloud would answer with that root's namespaces while
|
|
154
|
-
* every warrant claimed somebody else's authorship.
|
|
155
|
-
*/
|
|
156
|
-
export async function connectCloudWithAccount(options) {
|
|
157
|
-
const { root, authorAccount, ...rest } = options;
|
|
158
|
-
// Both keys resolved before the sign-in, for the reason `connectCloud` gives:
|
|
159
|
-
// a session minted for a connection that cannot be built is a credential
|
|
160
|
-
// issued for nothing.
|
|
161
|
-
const signer = await resolveSigner(rest.deviceSecret, rest.signer, 'deviceSecret');
|
|
162
|
-
const resolved = await resolveRoot(root);
|
|
163
|
-
const cloud = new CloudClient({
|
|
164
|
-
cloudBaseUrl: options.cloudBaseUrl,
|
|
165
|
-
// Reported here as well as on the connection's own client: this is where
|
|
166
|
-
// the session is first minted, and a caller persisting it would otherwise
|
|
167
|
-
// only hear about the rolling refreshes of a token it never saw.
|
|
168
|
-
onSession: options.onSession,
|
|
169
|
-
fetch: options.fetch,
|
|
170
|
-
timeoutMs: options.timeoutMs,
|
|
171
|
-
});
|
|
172
|
-
const session = await cloud.signInWithAccount(resolved.signer);
|
|
173
|
-
return connectCloud({
|
|
174
|
-
...rest,
|
|
175
|
-
// Already resolved above, and passing both forms on would be refused.
|
|
176
|
-
deviceSecret: undefined,
|
|
177
|
-
signer,
|
|
178
|
-
sessionToken: session.sessionToken,
|
|
179
|
-
authorAccount: authorAccount ?? resolved.accountId,
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
/** The one namespace to write in, or an error naming the actual ambiguity. */
|
|
183
|
-
function pickNamespace(namespaces, requested) {
|
|
184
|
-
if (requested) {
|
|
185
|
-
const found = namespaces.find((n) => n.namespaceId === requested);
|
|
186
|
-
if (!found) {
|
|
187
|
-
throw new Error(`this account owns no namespace ${requested}; it owns: ${namespaces.map((n) => n.namespaceId).join(', ') || '(none)'}`);
|
|
188
|
-
}
|
|
189
|
-
return found;
|
|
190
|
-
}
|
|
191
|
-
if (namespaces.length === 0) {
|
|
192
|
-
throw new Error('this account owns no namespaces in the cloud: claim one first, then enable HA on it so a relay is assigned');
|
|
193
|
-
}
|
|
194
|
-
if (namespaces.length > 1) {
|
|
195
|
-
throw new Error(`this account owns ${namespaces.length} namespaces — pass namespaceId to say which: ${namespaces
|
|
196
|
-
.map((n) => n.namespaceId)
|
|
197
|
-
.join(', ')}`);
|
|
198
|
-
}
|
|
199
|
-
return namespaces[0];
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Why no relay can write here — the states, kept apart.
|
|
203
|
-
*
|
|
204
|
-
* They are different asks of the user (turn HA on, wait, have an admin admit
|
|
205
|
-
* TEEs as relays, or have an admin grant a capability) and one message for all
|
|
206
|
-
* of them would send them to the wrong one.
|
|
207
|
-
*/
|
|
208
|
-
function explainNoRelay(namespace, relays) {
|
|
209
|
-
if (relays.length === 0) {
|
|
210
|
-
return namespace.haStatus === 'enabled'
|
|
211
|
-
? `namespace ${namespace.namespaceId} has HA enabled but no relay has been assigned to it yet — retry shortly`
|
|
212
|
-
: `namespace ${namespace.namespaceId} has no relays: enable HA on it in the cloud to have an attested node assigned`;
|
|
213
|
-
}
|
|
214
|
-
// A node that reports a role is decided by it alone: a `ReadOnlyTee` is a
|
|
215
|
-
// replica and never relays, whatever its grant, so asking for the grant would
|
|
216
|
-
// send the admin to the wrong fix. Only a node too old to report a role is
|
|
217
|
-
// still waiting on `CAN_AUTHOR_ON_BEHALF`.
|
|
218
|
-
const replicas = relays.filter((r) => r.teeRole !== null && r.teeRole !== 'RelayTee');
|
|
219
|
-
if (replicas.length === relays.length) {
|
|
220
|
-
return (`every node on namespace ${namespace.namespaceId} is a read-only TEE replica (ReadOnlyTee), ` +
|
|
221
|
-
`which never relays delegated writes: an admin of the namespace must set its TEE admission ` +
|
|
222
|
-
`policy with mode: 'relay' — AdminClient.setTeeAdmissionPolicy(namespaceId, { ..., mode: 'relay' }) ` +
|
|
223
|
-
`— so fleet nodes are admitted as RelayTee`);
|
|
224
|
-
}
|
|
225
|
-
const waitingOnGrant = relays.filter((r) => r.teeRole === null && !r.authorshipReady);
|
|
226
|
-
if (waitingOnGrant.length > 0 && waitingOnGrant.length + replicas.length === relays.length) {
|
|
227
|
-
// Name the call, not just the capability. The grant is a governance op only
|
|
228
|
-
// a namespace admin can publish, so this message is read by someone who has
|
|
229
|
-
// to go ask for it — and "grant CAN_AUTHOR_ON_BEHALF" alone leaves them
|
|
230
|
-
// relaying a constant to an admin who then has to work out the command.
|
|
231
|
-
// `openToDelegatedExecution` is the durable half (every relay assigned from
|
|
232
|
-
// then on lands open), `grantAuthorship` the one for relays already here.
|
|
233
|
-
return (`every relay on namespace ${namespace.namespaceId} is waiting for the authorship grant: ` +
|
|
234
|
-
`an admin of the namespace must grant CAN_AUTHOR_ON_BEHALF to ${waitingOnGrant.map((r) => r.executorAccount ?? r.peerId).join(', ') || 'the relay account'} — with AdminClient.grantAuthorship(namespaceId, account) for these relays, and ` +
|
|
235
|
-
`AdminClient.openToDelegatedExecution(namespaceId) so relays assigned later need no further op`);
|
|
236
|
-
}
|
|
237
|
-
return `namespace ${namespace.namespaceId} has ${relays.length} relay(s), but none is ready to relay yet (a URL, an executor account and a recent heartbeat) — retry shortly`;
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* A persisted nonce source keyed by the author's device public key.
|
|
241
|
-
*
|
|
242
|
-
* The key is the *public* key: a per-device key is required (two devices of one
|
|
243
|
-
* account are independent replicas) and the secret must never end up in a
|
|
244
|
-
* storage key an extension or another script can enumerate. The signer names
|
|
245
|
-
* its own public key, which is also the only form available when the private
|
|
246
|
-
* half cannot be exported.
|
|
247
|
-
*
|
|
248
|
-
* Falls back to memory where there is no `localStorage` — Node, a worker, an
|
|
249
|
-
* edge runtime. Those are processes that own their whole sequence, which is
|
|
250
|
-
* exactly the case a memory counter is correct for.
|
|
251
|
-
*/
|
|
252
|
-
function defaultNonceSource(devicePublicKey) {
|
|
253
|
-
const key = `mero-js:warrant-nonce:${devicePublicKey}`;
|
|
254
|
-
try {
|
|
255
|
-
return createLocalStorageNonceSource(key);
|
|
256
|
-
}
|
|
257
|
-
catch {
|
|
258
|
-
return createMemoryNonceSource(1);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Getting an app's device key certified **by a wallet on another origin**.
|
|
3
|
-
*
|
|
4
|
-
* An app holds a device keypair and no account. It cannot certify that key
|
|
5
|
-
* itself: certifying is a signature by an account root, and the root is the one
|
|
6
|
-
* key in the system with nothing above it to revoke it — so a page that can
|
|
7
|
-
* reach it can take the account for good. The whole design is that the app
|
|
8
|
-
* never can.
|
|
9
|
-
*
|
|
10
|
-
* What separates them is the **same-origin policy**, and that is why this is a
|
|
11
|
-
* redirect rather than a message, a popup API or a shared library. The wallet
|
|
12
|
-
* keeps the root as a non-extractable key in *its* origin's IndexedDB; script
|
|
13
|
-
* running on the app's origin cannot read another origin's storage, cannot read
|
|
14
|
-
* its keys, and cannot draw its consent screen. Nothing weaker enforces that —
|
|
15
|
-
* an iframe with a `postMessage` bridge would put the approval dialog inside a
|
|
16
|
-
* document the app can style, overlay and click for you.
|
|
17
|
-
*
|
|
18
|
-
* So the shape is: send the person to the wallet naming the key you want
|
|
19
|
-
* certified, they approve there, and the wallet redirects back with an
|
|
20
|
-
* `AccountProof<DeviceCert>` over that key.
|
|
21
|
-
*
|
|
22
|
-
* ## The credential comes back in the FRAGMENT
|
|
23
|
-
*
|
|
24
|
-
* Same reason the cloud's link grant does — see `link-redirect.ts`. A fragment
|
|
25
|
-
* is not sent to the server, so it lands in nobody's access log, no referrer
|
|
26
|
-
* header and no proxy trace on the way. A query string would put a credential
|
|
27
|
-
* into every log between here and the app's host.
|
|
28
|
-
*
|
|
29
|
-
* ## What the app must check before storing anything
|
|
30
|
-
*
|
|
31
|
-
* A fragment is written by whoever sent the person to this URL, which is not
|
|
32
|
-
* necessarily the wallet. Three things therefore have to be checked locally:
|
|
33
|
-
*
|
|
34
|
-
* - the credential certifies **the key this app asked about** — otherwise the
|
|
35
|
-
* app stores a credential it cannot sign for, and every warrant it mints is
|
|
36
|
-
* refused somewhere far from here;
|
|
37
|
-
* - the credential is **internally consistent** — the root signed it, the
|
|
38
|
-
* account it names is the one that root derives, and its device id was
|
|
39
|
-
* minted for that account ({@link verifyDeviceCredential});
|
|
40
|
-
* - the account reported beside it is **the account the credential names** —
|
|
41
|
-
* the app shows that id to a person and keys state by it, so a mismatch is a
|
|
42
|
-
* credential filed under someone else's name.
|
|
43
|
-
*
|
|
44
|
-
* {@link completeDeviceEnrolment} does all three and throws on any of them,
|
|
45
|
-
* before the caller has anything to store. What none of them establish is that
|
|
46
|
-
* the account is the *person's* — anyone can mint a root offline and certify
|
|
47
|
-
* any public key with it, and a device key is public once enrolled. That is
|
|
48
|
-
* what the state narrows: {@link deviceEnrolmentUrl} remembers each request, and
|
|
49
|
-
* {@link completeDeviceEnrolment} refuses an answer to a request this browser did
|
|
50
|
-
* not start, rather than to a link someone sent.
|
|
51
|
-
*/
|
|
52
|
-
import { type DeviceCredential } from '../device-cert/index.js';
|
|
53
|
-
export interface DeviceEnrolmentOptions {
|
|
54
|
-
/**
|
|
55
|
-
* The wallet's origin — a different origin from this app's, and the reason
|
|
56
|
-
* the flow exists. Pointing it at this app's own origin is accepted (a
|
|
57
|
-
* wallet may be hosted anywhere) but certifies nothing the app could not
|
|
58
|
-
* already do.
|
|
59
|
-
*/
|
|
60
|
-
walletUrl: string;
|
|
61
|
-
/** The device's ed25519 signing key to be certified, 64 hex. */
|
|
62
|
-
devicePublicKey: string;
|
|
63
|
-
/**
|
|
64
|
-
* The device's X25519 key-delivery key, 64 hex.
|
|
65
|
-
*
|
|
66
|
-
* Required, not optional: a certificate covers both keys, and one minted
|
|
67
|
-
* without a delivery key names a device no group key can ever be sealed to.
|
|
68
|
-
* Generate it alongside the signing key rather than enrolling twice.
|
|
69
|
-
*/
|
|
70
|
-
kemPublicKey: string;
|
|
71
|
-
/**
|
|
72
|
-
* Where the wallet sends them back. Must be `https:`, or `http:` on
|
|
73
|
-
* localhost — see {@link assertSafeReturnTo}.
|
|
74
|
-
*/
|
|
75
|
-
returnTo: string;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* The URL to send a person to.
|
|
79
|
-
*
|
|
80
|
-
* Built rather than documented as a string so the parameter names live in one
|
|
81
|
-
* place: a wallet and an app that disagree about them fail as a blank consent
|
|
82
|
-
* screen, which looks like an outage rather than a contract mismatch.
|
|
83
|
-
*
|
|
84
|
-
* Remembers the request under a fresh state the wallet echoes back, so
|
|
85
|
-
* {@link completeDeviceEnrolment} can match the answer to it.
|
|
86
|
-
*/
|
|
87
|
-
export declare function deviceEnrolmentUrl(options: DeviceEnrolmentOptions): string;
|
|
88
|
-
/** Send the person to the wallet to approve this device. Does not return. */
|
|
89
|
-
export declare function beginDeviceEnrolment(options: DeviceEnrolmentOptions): never;
|
|
90
|
-
/** What came back in the fragment, unverified. */
|
|
91
|
-
export interface DeviceEnrolmentCallback {
|
|
92
|
-
/** The `AccountProof<DeviceCert>`, hex borsh — as `signDeviceCert` returns it. */
|
|
93
|
-
credential: string;
|
|
94
|
-
/** The account the wallet says certified this device, 64 hex. */
|
|
95
|
-
account: string;
|
|
96
|
-
/** The device id the wallet minted, 64 hex. */
|
|
97
|
-
device: string;
|
|
98
|
-
/** The state the wallet echoed back from {@link deviceEnrolmentUrl}. */
|
|
99
|
-
state?: string;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Read an enrolment out of the current URL fragment, and strip it.
|
|
103
|
-
*
|
|
104
|
-
* Returns `null` on an ordinary page load, so a caller can run it
|
|
105
|
-
* unconditionally at startup. Throws when the person declined, because that is
|
|
106
|
-
* an outcome the app has to show rather than a state to wait in.
|
|
107
|
-
*
|
|
108
|
-
* Nothing is verified here — the shape is separated from the checking so a
|
|
109
|
-
* caller can see what arrived (to log it, or to name the account in a refusal)
|
|
110
|
-
* even when it goes on to be refused. Nothing read out of this belongs in
|
|
111
|
-
* storage until {@link completeDeviceEnrolment} has passed.
|
|
112
|
-
*/
|
|
113
|
-
export declare function readEnrolmentCallback(location?: {
|
|
114
|
-
hash: string;
|
|
115
|
-
}): DeviceEnrolmentCallback | null;
|
|
116
|
-
export interface CompleteDeviceEnrolmentOptions extends DeviceEnrolmentCallback {
|
|
117
|
-
/**
|
|
118
|
-
* The signing key this app asked to have certified, 64 hex.
|
|
119
|
-
*
|
|
120
|
-
* The binding that makes the returned credential usable: it must be *this*
|
|
121
|
-
* app's key, held in this app, or nothing it signs verifies against the
|
|
122
|
-
* certificate it presents.
|
|
123
|
-
*/
|
|
124
|
-
devicePublicKey: string;
|
|
125
|
-
/** The delivery key asked about, 64 hex. Checked when supplied. */
|
|
126
|
-
kemPublicKey?: string;
|
|
127
|
-
}
|
|
128
|
-
/** A device this app may now sign as. */
|
|
129
|
-
export interface EnrolledDevice {
|
|
130
|
-
/** The credential to present, hex — `RelayClientConfig.authorProof`. */
|
|
131
|
-
credential: string;
|
|
132
|
-
/** The account it belongs to, 64 hex. */
|
|
133
|
-
account: string;
|
|
134
|
-
/** The device id within that account, 64 hex. */
|
|
135
|
-
device: string;
|
|
136
|
-
/** Everything the certificate says, already checked. */
|
|
137
|
-
certificate: DeviceCredential;
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Verify what came back, and refuse loudly rather than returning a half-checked
|
|
141
|
-
* credential.
|
|
142
|
-
*
|
|
143
|
-
* Every throw here is a credential that must not reach storage. The order is
|
|
144
|
-
* deliberate: the cheap local comparisons first, so a fragment aimed at the
|
|
145
|
-
* wrong app is rejected without a signature verification, and the message names
|
|
146
|
-
* what disagreed rather than reporting "invalid credential".
|
|
147
|
-
*
|
|
148
|
-
* The state is spent on the first call, so an answer is accepted at most once.
|
|
149
|
-
*/
|
|
150
|
-
export declare function completeDeviceEnrolment(options: CompleteDeviceEnrolmentOptions): Promise<EnrolledDevice>;
|
|
151
|
-
/**
|
|
152
|
-
* Where a credential may be delivered.
|
|
153
|
-
*
|
|
154
|
-
* `https:` anywhere, `http:` only on loopback. Plain HTTP elsewhere would put
|
|
155
|
-
* the fragment on a page any network position can rewrite, and the fragment is
|
|
156
|
-
* the credential; loopback is exempt because it never leaves the machine and is
|
|
157
|
-
* the only way to develop against a wallet at all.
|
|
158
|
-
*
|
|
159
|
-
* Everything else — `javascript:`, `data:`, a custom app scheme — is refused.
|
|
160
|
-
* The first two are script the wallet would be asked to run; a custom scheme is
|
|
161
|
-
* a legitimate thing to want for a native app, but which installed handler
|
|
162
|
-
* claims it is not something this SDK can check, so it is refused here rather
|
|
163
|
-
* than accepted on trust.
|
|
164
|
-
*/
|
|
165
|
-
/**
|
|
166
|
-
* Is the credential coming back to somewhere other than the page that asked?
|
|
167
|
-
*
|
|
168
|
-
* Normally an app enrols its own device key and gets the answer back on its own
|
|
169
|
-
* origin, so the two match. They can legitimately differ — a callback page on a
|
|
170
|
-
* sibling host, an app mid-migration — which is why this is not refused.
|
|
171
|
-
*
|
|
172
|
-
* But it is the shape an abuse would also take: send someone to the real wallet
|
|
173
|
-
* and have the credential delivered elsewhere. Nothing here can tell the two
|
|
174
|
-
* apart, and a client-side check constrains nobody who did not call this
|
|
175
|
-
* function anyway. What it is good for is the honest case — surfacing the
|
|
176
|
-
* mismatch to whoever is building the app, and giving a wallet the same fact to
|
|
177
|
-
* put in front of the person approving.
|
|
178
|
-
*
|
|
179
|
-
* Exported so a wallet can render it, rather than each caller re-deriving it.
|
|
180
|
-
*/
|
|
181
|
-
export declare function callbackIsForeign(returnTo: string, pageOrigin?: string): boolean;
|