@cef-ai/wallet-identity 1.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 +64 -0
- package/dist/index.cjs +690 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +443 -0
- package/dist/index.d.ts +443 -0
- package/dist/index.js +667 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
import { ApiClient } from '@cef-ai/wallet-api-client';
|
|
2
|
+
|
|
3
|
+
/** Spec §3.1. Public contract for the identity layer. */
|
|
4
|
+
interface Addresses {
|
|
5
|
+
cere: string;
|
|
6
|
+
solana: string;
|
|
7
|
+
evm: string;
|
|
8
|
+
}
|
|
9
|
+
type Chain = 'cere' | 'solana' | 'evm';
|
|
10
|
+
interface Identity {
|
|
11
|
+
readonly isAuthenticated: boolean;
|
|
12
|
+
readonly addresses: Addresses | null;
|
|
13
|
+
readonly credentialId: string | null;
|
|
14
|
+
register(opts?: {
|
|
15
|
+
label?: string;
|
|
16
|
+
}): Promise<void>;
|
|
17
|
+
login(): Promise<void>;
|
|
18
|
+
logout(): void;
|
|
19
|
+
/** Returns a valid JWT; runs the passkey ceremony if needed. */
|
|
20
|
+
getJwt(): Promise<string>;
|
|
21
|
+
/**
|
|
22
|
+
* Release any browser-owned handles this identity holds (cross-tab channel,
|
|
23
|
+
* timers, broadcast listeners). After `dispose()` the identity is unusable.
|
|
24
|
+
*
|
|
25
|
+
* Idempotent: callable multiple times safely.
|
|
26
|
+
*/
|
|
27
|
+
dispose(): void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Signers/* receive this function via a constructor-time closure handoff
|
|
31
|
+
* from a concrete Identity instance. It is exported as a type-only alias so
|
|
32
|
+
* dependents can annotate the closures they receive, but it is intentionally
|
|
33
|
+
* absent from the `Identity` interface — there is no public `sign()` method.
|
|
34
|
+
*/
|
|
35
|
+
type InternalSign = (chain: Chain, payload: Uint8Array) => Promise<Uint8Array>;
|
|
36
|
+
|
|
37
|
+
/** Spec §3.2 locked decision #14. Permanent for v1; rotate via "-v2" suffix. */
|
|
38
|
+
declare const PRF_INPUT_LABEL = "cere-wallet-prf-v1";
|
|
39
|
+
/**
|
|
40
|
+
* SHA-256("cere-wallet-prf-v1") — 32 bytes. Fed to WebAuthn `prf.eval.first`.
|
|
41
|
+
*
|
|
42
|
+
* Implementation note: `createHash` runs at module evaluation time. In the test
|
|
43
|
+
* environment (Vitest on Node) this works natively via `node:crypto`. In the
|
|
44
|
+
* browser this depends on a `node:crypto` polyfill — `vite-plugin-node-polyfills`
|
|
45
|
+
* is wired in by Cycle 1 Task 8. A follow-up cycle should replace this expression
|
|
46
|
+
* with a hard-coded `Uint8Array` literal to eliminate the polyfill dependency.
|
|
47
|
+
*/
|
|
48
|
+
declare const PRF_INPUT_SEED: Uint8Array;
|
|
49
|
+
/** Spec §3.2. Used as HKDF-SHA256 info to derive the secp256k1 32-byte secret. */
|
|
50
|
+
declare const EVM_HKDF_INFO = "cere-wallet-evm-secp256k1-v1";
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Runtime support detection for WebAuthn + PRF.
|
|
54
|
+
*
|
|
55
|
+
* Spec §3.7. Real PRF capability is only knowable from a completed ceremony
|
|
56
|
+
* (via `clientExtensionResults.prf.enabled` or `prf.results.first`). This
|
|
57
|
+
* pre-check returns the best signal we can get without spending a credential.
|
|
58
|
+
*/
|
|
59
|
+
interface PrfSupportResult {
|
|
60
|
+
webAuthn: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Whether a platform authenticator (Touch ID / Windows Hello) is enrolled.
|
|
63
|
+
* INFORMATIONAL ONLY — it is NOT a precondition for using the wallet, which
|
|
64
|
+
* also works with roaming security keys and phone passkeys (via hybrid).
|
|
65
|
+
*/
|
|
66
|
+
platformAuthenticator: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Best-effort PRF signal. `true` unless we can prove otherwise: modern
|
|
69
|
+
* browsers expose `PublicKeyCredential.getClientCapabilities()`, which gives
|
|
70
|
+
* a definitive `extensions:prf` answer. When that API is absent (older
|
|
71
|
+
* browsers) we stay optimistic and let the ceremony be the final arbiter
|
|
72
|
+
* (it throws `prf-unsupported` if the authenticator returns no PRF output).
|
|
73
|
+
*/
|
|
74
|
+
prfPotentiallySupported: boolean;
|
|
75
|
+
}
|
|
76
|
+
declare function detectPrfSupport(): Promise<PrfSupportResult>;
|
|
77
|
+
/**
|
|
78
|
+
* Inspect a `PublicKeyCredential.getClientExtensionResults()` payload to
|
|
79
|
+
* determine whether PRF actually worked in the ceremony. Spec §3.7 calls this
|
|
80
|
+
* "final answer; the pre-check above is best-effort only".
|
|
81
|
+
*/
|
|
82
|
+
declare function isPrfSupportedResult(extensions: {
|
|
83
|
+
prf?: {
|
|
84
|
+
enabled?: boolean;
|
|
85
|
+
results?: {
|
|
86
|
+
first?: Uint8Array;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
}): boolean;
|
|
90
|
+
|
|
91
|
+
/** Cere's SS58 network prefix. Spec §3.2. */
|
|
92
|
+
declare const CERE_SS58_PREFIX = 54;
|
|
93
|
+
/**
|
|
94
|
+
* SS58 address for the Cere network.
|
|
95
|
+
*
|
|
96
|
+
* SS58 spec (Substrate): for prefixes 0–63 the format is:
|
|
97
|
+
* [prefix byte] || [32-byte pubkey] || [2-byte checksum]
|
|
98
|
+
* where checksum = blake2b-512("SS58PRE" || prefix || pubkey)[0..2].
|
|
99
|
+
* The whole thing is base58-encoded. We use prefix 54 (Cere) which fits in
|
|
100
|
+
* the 1-byte encoding (< 64).
|
|
101
|
+
*
|
|
102
|
+
* @param edPubkey 32-byte Ed25519 public key.
|
|
103
|
+
*/
|
|
104
|
+
declare function deriveCereAddress(edPubkey: Uint8Array): string;
|
|
105
|
+
/**
|
|
106
|
+
* Solana address: base58(Ed25519 pubkey). No prefix, no checksum.
|
|
107
|
+
*
|
|
108
|
+
* @param edPubkey 32-byte Ed25519 public key.
|
|
109
|
+
*/
|
|
110
|
+
declare function deriveSolanaAddress(edPubkey: Uint8Array): string;
|
|
111
|
+
/**
|
|
112
|
+
* Recover the raw 32-byte Ed25519 public key from a Solana address.
|
|
113
|
+
*
|
|
114
|
+
* The Solana address is `base58(edPubkey)` with no prefix or checksum
|
|
115
|
+
* ({@link deriveSolanaAddress}), so a base58 decode yields the pubkey directly.
|
|
116
|
+
* The public key is not secret — it IS the address — so this introduces no key
|
|
117
|
+
* exposure beyond what the address already reveals; the session keys never
|
|
118
|
+
* leave `SessionVault`. The same Ed25519 key backs the Cere SS58 address.
|
|
119
|
+
*
|
|
120
|
+
* @param solanaAddress base58 Solana address.
|
|
121
|
+
* @returns the 32-byte Ed25519 public key.
|
|
122
|
+
*/
|
|
123
|
+
declare function decodeEd25519Pubkey(solanaAddress: string): Uint8Array;
|
|
124
|
+
/**
|
|
125
|
+
* EVM address: '0x' + last 20 bytes of keccak256(secp256k1 uncompressed pubkey X||Y).
|
|
126
|
+
*
|
|
127
|
+
* Accepts either 64-byte (raw X||Y) or 65-byte (0x04||X||Y) input. The 0x04
|
|
128
|
+
* prefix byte is stripped if present.
|
|
129
|
+
*
|
|
130
|
+
* @param secpPubkey secp256k1 public key.
|
|
131
|
+
*/
|
|
132
|
+
declare function deriveEvmAddress(secpPubkey: Uint8Array): string;
|
|
133
|
+
|
|
134
|
+
interface DerivedKeys {
|
|
135
|
+
/** Ed25519 32-byte secret key seed; used for Cere + Solana signing. */
|
|
136
|
+
edSeed: Uint8Array;
|
|
137
|
+
/** secp256k1 32-byte secret key; used for EVM signing. */
|
|
138
|
+
secpKey: Uint8Array;
|
|
139
|
+
/** 32-byte Ed25519 public key derived from edSeed. */
|
|
140
|
+
edPubkey: Uint8Array;
|
|
141
|
+
/** 65-byte uncompressed secp256k1 public key (0x04 || X || Y) derived from secpKey. */
|
|
142
|
+
secpPubkeyUncompressed: Uint8Array;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Derive the v2 session keys from a WebAuthn PRF output. Spec §3.2.
|
|
146
|
+
*
|
|
147
|
+
* edSeed = prfOutput
|
|
148
|
+
* secpKey = HKDF-SHA256(prfOutput, salt="", info="cere-wallet-evm-secp256k1-v1", L=32)
|
|
149
|
+
*
|
|
150
|
+
* @param prfOutput 32-byte PRF output from `prf.results.first`.
|
|
151
|
+
*/
|
|
152
|
+
declare function derivedKeys(prfOutput: Uint8Array): DerivedKeys;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* `hints` is a top-level field on the WebAuthn creation/request options
|
|
156
|
+
* (WebAuthn L3), but our TS DOM lib doesn't ship it yet. Declare-merge the
|
|
157
|
+
* precise W3C union so the ceremony can request authenticator hints without
|
|
158
|
+
* casts. Drop this augmentation once the workspace lib.dom.d.ts ships `hints`.
|
|
159
|
+
*/
|
|
160
|
+
declare global {
|
|
161
|
+
interface PublicKeyCredentialCreationOptions {
|
|
162
|
+
hints?: ('client-device' | 'hybrid' | 'security-key')[];
|
|
163
|
+
}
|
|
164
|
+
interface PublicKeyCredentialRequestOptions {
|
|
165
|
+
hints?: ('client-device' | 'hybrid' | 'security-key')[];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Adapter that runs WebAuthn ceremonies. Production implementations call
|
|
170
|
+
* `navigator.credentials.create/get`; the test implementation in
|
|
171
|
+
* `SoftAuthenticator` produces deterministic results without browser APIs.
|
|
172
|
+
*
|
|
173
|
+
* Spec §3.7. The `prfOutput` field is the result of the WebAuthn PRF extension
|
|
174
|
+
* — present when the authenticator supports PRF, absent otherwise.
|
|
175
|
+
*/
|
|
176
|
+
interface CeremonyAdapter {
|
|
177
|
+
register(opts: RegisterOptions): Promise<RegisterResult>;
|
|
178
|
+
login(opts: LoginOptions): Promise<LoginResult>;
|
|
179
|
+
}
|
|
180
|
+
interface RegisterOptions {
|
|
181
|
+
rpId: string;
|
|
182
|
+
challenge: Uint8Array;
|
|
183
|
+
userHandle: Uint8Array;
|
|
184
|
+
/** 32-byte PRF input (typically `PRF_INPUT_SEED`). */
|
|
185
|
+
prfInput: Uint8Array;
|
|
186
|
+
/** Human-readable label for the credential. Currently a hint, not stored. */
|
|
187
|
+
label?: string;
|
|
188
|
+
}
|
|
189
|
+
interface RegisterResult {
|
|
190
|
+
credentialId: string;
|
|
191
|
+
clientDataJSON: string;
|
|
192
|
+
attestationObject: string;
|
|
193
|
+
transports: string[];
|
|
194
|
+
/** Present iff PRF extension worked. */
|
|
195
|
+
prfOutput?: Uint8Array;
|
|
196
|
+
}
|
|
197
|
+
interface LoginOptions {
|
|
198
|
+
rpId: string;
|
|
199
|
+
challenge: Uint8Array;
|
|
200
|
+
/** Optional: if absent, the authenticator picks a credential. Present from us. */
|
|
201
|
+
credentialId?: string;
|
|
202
|
+
prfInput: Uint8Array;
|
|
203
|
+
}
|
|
204
|
+
interface LoginResult {
|
|
205
|
+
credentialId: string;
|
|
206
|
+
clientDataJSON: string;
|
|
207
|
+
authenticatorData: string;
|
|
208
|
+
signature: string;
|
|
209
|
+
prfOutput?: Uint8Array;
|
|
210
|
+
}
|
|
211
|
+
interface WebAuthnCeremonyAdapterOptions {
|
|
212
|
+
/**
|
|
213
|
+
* Override for the WebAuthn credentials manager. Defaults to
|
|
214
|
+
* `globalThis.navigator?.credentials`. Tests inject a mock.
|
|
215
|
+
*/
|
|
216
|
+
credentials?: CredentialsContainer;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Production WebAuthn ceremony adapter. Calls `navigator.credentials.create/get`
|
|
220
|
+
* with the PRF extension. Spec §3.7.
|
|
221
|
+
*
|
|
222
|
+
* - `register`: requires a platform authenticator, user verification, Ed25519
|
|
223
|
+
* (`alg: -8`), and the PRF extension with the wallet's input seed.
|
|
224
|
+
* - `login`: allows a specific credentialId if known; otherwise lets the
|
|
225
|
+
* authenticator pick (resident key).
|
|
226
|
+
*
|
|
227
|
+
* Returned ArrayBuffers are base64url-encoded for API transport.
|
|
228
|
+
*/
|
|
229
|
+
declare class WebAuthnCeremonyAdapter implements CeremonyAdapter {
|
|
230
|
+
private readonly credentials?;
|
|
231
|
+
constructor(opts?: WebAuthnCeremonyAdapterOptions);
|
|
232
|
+
register(opts: RegisterOptions): Promise<RegisterResult>;
|
|
233
|
+
login(opts: LoginOptions): Promise<LoginResult>;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Base64url (RFC 4648 §5) helpers used by the WebAuthn ceremony adapter.
|
|
238
|
+
* Trailing `=` padding is stripped on encode; both padded and unpadded inputs
|
|
239
|
+
* are accepted on decode.
|
|
240
|
+
*/
|
|
241
|
+
/** Encode a Uint8Array or ArrayBuffer as a base64url string (no padding). */
|
|
242
|
+
declare function bytesToB64u(input: Uint8Array | ArrayBuffer): string;
|
|
243
|
+
/** Decode a base64url string into a Uint8Array. Padding is optional. */
|
|
244
|
+
declare function b64uToBytes(s: string): Uint8Array;
|
|
245
|
+
|
|
246
|
+
interface SoftAuthenticatorOptions {
|
|
247
|
+
/**
|
|
248
|
+
* If provided, derive the per-registration `edSeed`, `prfSecret`, and
|
|
249
|
+
* `credentialId` deterministically from this seed (mixed with a monotonic
|
|
250
|
+
* counter so multiple register() calls on the same instance still produce
|
|
251
|
+
* distinct credentials). Used by Playwright e2e specs that pin server-side
|
|
252
|
+
* address stubs against the SoftAuthenticator's output. Production code
|
|
253
|
+
* never sets this — the `useSoft` branch in `createIdentity.ts` is itself
|
|
254
|
+
* forbidden in production builds.
|
|
255
|
+
*
|
|
256
|
+
* Default: undefined (each register() draws fresh CSPRNG bytes, matching
|
|
257
|
+
* the pre-cycle-8.1 behavior used by unit tests in `ceremony.test.ts`).
|
|
258
|
+
*/
|
|
259
|
+
seed?: string;
|
|
260
|
+
/**
|
|
261
|
+
* When the caller invokes login() without a credentialId, fall back to the
|
|
262
|
+
* most-recently-registered credential in memory. Default: false (login()
|
|
263
|
+
* throws when credentialId is missing, matching pre-cycle-8 behavior).
|
|
264
|
+
*
|
|
265
|
+
* Required for e2e flows that simulate the user signing back in after the
|
|
266
|
+
* vault has been cleared (post-logout re-login): the wallet has discarded
|
|
267
|
+
* the credentialId locally, but the soft authenticator's in-memory creds
|
|
268
|
+
* map still holds the registration, and the spec test needs login() to
|
|
269
|
+
* succeed anyway. Production code must never enable this — real WebAuthn's
|
|
270
|
+
* no-allowCredentials path shows an OS credential picker; the fallback
|
|
271
|
+
* here is a deterministic test convenience, not a real picker, so leaving
|
|
272
|
+
* it on by default would silently let a single registered credential
|
|
273
|
+
* impersonate any login.
|
|
274
|
+
*/
|
|
275
|
+
fallbackToLastRegistered?: boolean;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Deterministic in-memory WebAuthn authenticator for tests. NOT for production.
|
|
279
|
+
*
|
|
280
|
+
* - Stores credentials in a private Map keyed by credentialId.
|
|
281
|
+
* - `prfOutput` is computed as HKDF-SHA256(prfSecret, salt="", info=prfInput, L=32),
|
|
282
|
+
* so for a given credential and prfInput the result is stable across login calls.
|
|
283
|
+
* - `signature` is a valid Ed25519 signature over `authenticatorData || sha256(clientDataJSON)`.
|
|
284
|
+
*
|
|
285
|
+
* The output shapes match the real WebAuthn response well enough that the
|
|
286
|
+
* `Identity` integration tests (Task 6) can drive register → login → derive
|
|
287
|
+
* → cross-check without a real browser.
|
|
288
|
+
*/
|
|
289
|
+
declare class SoftAuthenticator implements CeremonyAdapter {
|
|
290
|
+
private readonly creds;
|
|
291
|
+
private readonly seedBytes;
|
|
292
|
+
private readonly fallbackToLastRegistered;
|
|
293
|
+
/** Monotonic counter mixed into the seed-derived material so successive
|
|
294
|
+
* register() calls on a seeded instance produce distinct credentials. */
|
|
295
|
+
private regCounter;
|
|
296
|
+
constructor(opts?: SoftAuthenticatorOptions);
|
|
297
|
+
register(opts: RegisterOptions): Promise<RegisterResult>;
|
|
298
|
+
login(opts: LoginOptions): Promise<LoginResult>;
|
|
299
|
+
/** Public-key bytes for a credential. Used by integration tests for cross-checks. */
|
|
300
|
+
getEd25519PublicKey(credentialId: string): Uint8Array;
|
|
301
|
+
private computePrf;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
interface VaultSnapshot {
|
|
305
|
+
addresses: Addresses;
|
|
306
|
+
credentialId: string;
|
|
307
|
+
}
|
|
308
|
+
interface SessionVault {
|
|
309
|
+
isOpen(): boolean;
|
|
310
|
+
/** Public-safe view: never includes raw keys. */
|
|
311
|
+
snapshot(): VaultSnapshot | null;
|
|
312
|
+
/** Open the vault with derived keys + the addresses they yielded + the credentialId. */
|
|
313
|
+
set(args: {
|
|
314
|
+
keys: DerivedKeys;
|
|
315
|
+
addresses: Addresses;
|
|
316
|
+
credentialId: string;
|
|
317
|
+
}): void;
|
|
318
|
+
/** Drop the keys and address state. Safe to call when already closed. */
|
|
319
|
+
clear(): void;
|
|
320
|
+
/**
|
|
321
|
+
* Sign a payload with the chain-appropriate key. Spec §3.6 invariant:
|
|
322
|
+
* this is the only way the rest of the package can reach the keys.
|
|
323
|
+
*
|
|
324
|
+
* - 'cere' / 'solana': Ed25519 signature over the raw payload (64 bytes).
|
|
325
|
+
* - 'evm': secp256k1 ECDSA over the raw payload (65 bytes: r || s || v).
|
|
326
|
+
* The caller (an EVM signer) is responsible for framing (e.g. EIP-191)
|
|
327
|
+
* and hashing (keccak256) before passing the digest as the payload.
|
|
328
|
+
*/
|
|
329
|
+
sign(chain: Chain, payload: Uint8Array): Promise<Uint8Array>;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Closure-encapsulated session vault. The keys live in the closure variables
|
|
333
|
+
* `state.edSeed` / `state.secpKey` and are not reachable from outside.
|
|
334
|
+
*
|
|
335
|
+
* Spec §3.4. Lifetime: from `set()` until `clear()` or tab unload.
|
|
336
|
+
*/
|
|
337
|
+
declare function createSessionVault(): SessionVault;
|
|
338
|
+
|
|
339
|
+
interface IdentityImplOptions {
|
|
340
|
+
apiClient: ApiClient;
|
|
341
|
+
ceremony: CeremonyAdapter;
|
|
342
|
+
}
|
|
343
|
+
declare class IdentityImpl implements Identity {
|
|
344
|
+
private readonly opts;
|
|
345
|
+
private readonly vault;
|
|
346
|
+
private cachedJwt;
|
|
347
|
+
/** Server-returned credential ID (used as the public identity.credentialId). */
|
|
348
|
+
private serverCredentialId;
|
|
349
|
+
private readonly _isAuthenticated;
|
|
350
|
+
private readonly _addresses;
|
|
351
|
+
private readonly _credentialId;
|
|
352
|
+
/** Cross-tab logout coordination via BroadcastChannel('scp-wallet-v2'). */
|
|
353
|
+
private readonly crossTabSync;
|
|
354
|
+
/** Handle to detach the cross-tab logout subscriber on dispose(). */
|
|
355
|
+
private readonly crossTabSyncUnsubscribe;
|
|
356
|
+
/** Idempotency flag for dispose(). */
|
|
357
|
+
private disposed;
|
|
358
|
+
constructor(opts: IdentityImplOptions);
|
|
359
|
+
get isAuthenticated(): boolean;
|
|
360
|
+
get addresses(): Addresses | null;
|
|
361
|
+
/**
|
|
362
|
+
* Returns the server-returned credentialId from the last register/login ceremony.
|
|
363
|
+
* The vault internally tracks the authenticator-generated credential ID for login use.
|
|
364
|
+
*/
|
|
365
|
+
get credentialId(): string | null;
|
|
366
|
+
/**
|
|
367
|
+
* Read the (non-observable) vault snapshot + cached server credential ID and
|
|
368
|
+
* push the values into the three public observable boxes. Call after any
|
|
369
|
+
* mutation that could change the public derived state: register, login,
|
|
370
|
+
* logout, or constructor (hydration).
|
|
371
|
+
*/
|
|
372
|
+
private syncPublicState;
|
|
373
|
+
register(opts?: {
|
|
374
|
+
label?: string;
|
|
375
|
+
}): Promise<void>;
|
|
376
|
+
login(): Promise<void>;
|
|
377
|
+
logout(): void;
|
|
378
|
+
getJwt(): Promise<string>;
|
|
379
|
+
/** Helper used by callers (e.g. ApiClient.getAuthToken). */
|
|
380
|
+
getCachedJwtForApiClient(): string | null;
|
|
381
|
+
/**
|
|
382
|
+
* Returns the session vault. Used by popup-side `wallet:sign` handlers
|
|
383
|
+
* which need direct access to `vault.sign(chain, payload)`. NOT for host-
|
|
384
|
+
* side use — keys never reach the host. Spec §3.6 invariant 1.
|
|
385
|
+
*/
|
|
386
|
+
getVault(): SessionVault;
|
|
387
|
+
/**
|
|
388
|
+
* Release the BroadcastChannel + cross-tab subscriber. Idempotent.
|
|
389
|
+
*
|
|
390
|
+
* Called by:
|
|
391
|
+
* - WalletProvider's useEffect cleanup (SPA unmount / apiBaseUrl change)
|
|
392
|
+
* - test afterEach hooks (prevent listener leakage across tests)
|
|
393
|
+
*
|
|
394
|
+
* After dispose(), the identity is unusable: register/login/logout/getJwt
|
|
395
|
+
* still execute their normal logic, but cross-tab broadcasts no longer
|
|
396
|
+
* propagate (channel is closed) and the local subscriber is detached.
|
|
397
|
+
* The vault remains cleared (logout() runs locally). Spec §9.4 lifecycle.
|
|
398
|
+
*/
|
|
399
|
+
dispose(): void;
|
|
400
|
+
private crossCheckAddresses;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Cross-tab session synchronisation helper.
|
|
405
|
+
*
|
|
406
|
+
* Wraps a `BroadcastChannel` on a fixed channel name so all tabs of the same
|
|
407
|
+
* origin can coordinate. Today we only use it for `{ type: 'logout' }` — when
|
|
408
|
+
* one tab logs out, every other tab clears its in-memory vault + JWT so its
|
|
409
|
+
* MobX-reactive UI immediately reflects the logged-out state.
|
|
410
|
+
*
|
|
411
|
+
* The channel name must match `BROADCAST_CHANNEL_NAME` in
|
|
412
|
+
* `packages/embed-sdk/src/protocol.ts` ('scp-wallet-v2'). Defined here as a
|
|
413
|
+
* local constant rather than imported to avoid a circular dep between
|
|
414
|
+
* `@cef-ai/wallet-identity` and `@cef-ai/wallet`. The name is part of the
|
|
415
|
+
* wire protocol — if you change one, change the other (and bump the major).
|
|
416
|
+
*
|
|
417
|
+
* Spec §9.4 (cross-tab session sharing).
|
|
418
|
+
*/
|
|
419
|
+
type CrossTabMessage = {
|
|
420
|
+
type: 'logout';
|
|
421
|
+
};
|
|
422
|
+
declare class CrossTabSync {
|
|
423
|
+
private channel;
|
|
424
|
+
private readonly listeners;
|
|
425
|
+
constructor();
|
|
426
|
+
/** Broadcast a message to all other tabs (and not to this tab — that's the
|
|
427
|
+
* BroadcastChannel spec). The originating tab is expected to have already
|
|
428
|
+
* applied the state change locally before broadcasting. */
|
|
429
|
+
broadcast(msg: CrossTabMessage): void;
|
|
430
|
+
/** Subscribe to messages from other tabs. Returns an unsubscribe function. */
|
|
431
|
+
subscribe(fn: (msg: CrossTabMessage) => void): () => void;
|
|
432
|
+
/**
|
|
433
|
+
* Close the underlying channel and drop all listeners. Idempotent.
|
|
434
|
+
*
|
|
435
|
+
* Sets `this.channel = null` so subsequent `broadcast()` calls become a
|
|
436
|
+
* no-op (the `?.postMessage` short-circuits) and we don't accidentally
|
|
437
|
+
* postMessage on a closed BroadcastChannel (which throws InvalidStateError
|
|
438
|
+
* in some implementations).
|
|
439
|
+
*/
|
|
440
|
+
close(): void;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
export { type Addresses, CERE_SS58_PREFIX, type CeremonyAdapter, type Chain, type CrossTabMessage, CrossTabSync, type DerivedKeys, EVM_HKDF_INFO, type Identity, IdentityImpl, type IdentityImplOptions, type InternalSign, type LoginOptions, type LoginResult, PRF_INPUT_LABEL, PRF_INPUT_SEED, type PrfSupportResult, type RegisterOptions, type RegisterResult, type SessionVault, SoftAuthenticator, type VaultSnapshot, WebAuthnCeremonyAdapter, type WebAuthnCeremonyAdapterOptions, b64uToBytes, bytesToB64u, createSessionVault, decodeEd25519Pubkey, deriveCereAddress, deriveEvmAddress, deriveSolanaAddress, derivedKeys, detectPrfSupport, isPrfSupportedResult };
|