@interop/was-react 0.1.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/LICENSE.md +20 -0
- package/README.md +410 -0
- package/dist/auth/chapi.d.ts +43 -0
- package/dist/auth/chapi.d.ts.map +1 -0
- package/dist/auth/chapi.js +107 -0
- package/dist/auth/chapi.js.map +1 -0
- package/dist/auth/loginFlow.d.ts +93 -0
- package/dist/auth/loginFlow.d.ts.map +1 -0
- package/dist/auth/loginFlow.js +149 -0
- package/dist/auth/loginFlow.js.map +1 -0
- package/dist/auth/loginRequest.d.ts +66 -0
- package/dist/auth/loginRequest.d.ts.map +1 -0
- package/dist/auth/loginRequest.js +83 -0
- package/dist/auth/loginRequest.js.map +1 -0
- package/dist/auth/verifyResponse.d.ts +52 -0
- package/dist/auth/verifyResponse.d.ts.map +1 -0
- package/dist/auth/verifyResponse.js +138 -0
- package/dist/auth/verifyResponse.js.map +1 -0
- package/dist/auth/verifyResponse.test.d.ts +2 -0
- package/dist/auth/verifyResponse.test.d.ts.map +1 -0
- package/dist/auth/verifyResponse.test.js +282 -0
- package/dist/auth/verifyResponse.test.js.map +1 -0
- package/dist/auth/walletRequestTypes.d.ts +143 -0
- package/dist/auth/walletRequestTypes.d.ts.map +1 -0
- package/dist/auth/walletRequestTypes.js +2 -0
- package/dist/auth/walletRequestTypes.js.map +1 -0
- package/dist/config.d.ts +150 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +27 -0
- package/dist/config.js.map +1 -0
- package/dist/dev/cli.d.ts +16 -0
- package/dist/dev/cli.d.ts.map +1 -0
- package/dist/dev/cli.js +122 -0
- package/dist/dev/cli.js.map +1 -0
- package/dist/dev/cli.test.d.ts +2 -0
- package/dist/dev/cli.test.d.ts.map +1 -0
- package/dist/dev/cli.test.js +39 -0
- package/dist/dev/cli.test.js.map +1 -0
- package/dist/dev/index.d.ts +10 -0
- package/dist/dev/index.d.ts.map +1 -0
- package/dist/dev/index.js +10 -0
- package/dist/dev/index.js.map +1 -0
- package/dist/dev/provisionDevGrants.d.ts +73 -0
- package/dist/dev/provisionDevGrants.d.ts.map +1 -0
- package/dist/dev/provisionDevGrants.js +176 -0
- package/dist/dev/provisionDevGrants.js.map +1 -0
- package/dist/grants.d.ts +59 -0
- package/dist/grants.d.ts.map +1 -0
- package/dist/grants.js +82 -0
- package/dist/grants.js.map +1 -0
- package/dist/grants.test.d.ts +2 -0
- package/dist/grants.test.d.ts.map +1 -0
- package/dist/grants.test.js +79 -0
- package/dist/grants.test.js.map +1 -0
- package/dist/identity/agents.d.ts +99 -0
- package/dist/identity/agents.d.ts.map +1 -0
- package/dist/identity/agents.js +132 -0
- package/dist/identity/agents.js.map +1 -0
- package/dist/identity/appSession.d.ts +78 -0
- package/dist/identity/appSession.d.ts.map +1 -0
- package/dist/identity/appSession.js +93 -0
- package/dist/identity/appSession.js.map +1 -0
- package/dist/identity/documentLoader.d.ts +20 -0
- package/dist/identity/documentLoader.d.ts.map +1 -0
- package/dist/identity/documentLoader.js +99 -0
- package/dist/identity/documentLoader.js.map +1 -0
- package/dist/identity/initAppSession.d.ts +27 -0
- package/dist/identity/initAppSession.d.ts.map +1 -0
- package/dist/identity/initAppSession.js +30 -0
- package/dist/identity/initAppSession.js.map +1 -0
- package/dist/identity/seedCredential.d.ts +74 -0
- package/dist/identity/seedCredential.d.ts.map +1 -0
- package/dist/identity/seedCredential.js +165 -0
- package/dist/identity/seedCredential.js.map +1 -0
- package/dist/identity/seedCredential.test.d.ts +2 -0
- package/dist/identity/seedCredential.test.d.ts.map +1 -0
- package/dist/identity/seedCredential.test.js +205 -0
- package/dist/identity/seedCredential.test.js.map +1 -0
- package/dist/identity/seedStore.d.ts +27 -0
- package/dist/identity/seedStore.d.ts.map +1 -0
- package/dist/identity/seedStore.js +74 -0
- package/dist/identity/seedStore.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/mui/ProtectedRoute.d.ts +10 -0
- package/dist/mui/ProtectedRoute.d.ts.map +1 -0
- package/dist/mui/ProtectedRoute.js +54 -0
- package/dist/mui/ProtectedRoute.js.map +1 -0
- package/dist/mui/ReconnectBanner.d.ts +2 -0
- package/dist/mui/ReconnectBanner.d.ts.map +1 -0
- package/dist/mui/ReconnectBanner.js +20 -0
- package/dist/mui/ReconnectBanner.js.map +1 -0
- package/dist/mui/SyncStatusChip.d.ts +2 -0
- package/dist/mui/SyncStatusChip.d.ts.map +1 -0
- package/dist/mui/SyncStatusChip.js +27 -0
- package/dist/mui/SyncStatusChip.js.map +1 -0
- package/dist/mui/index.d.ts +12 -0
- package/dist/mui/index.d.ts.map +1 -0
- package/dist/mui/index.js +12 -0
- package/dist/mui/index.js.map +1 -0
- package/dist/react/WasSessionProvider.d.ts +36 -0
- package/dist/react/WasSessionProvider.d.ts.map +1 -0
- package/dist/react/WasSessionProvider.js +35 -0
- package/dist/react/WasSessionProvider.js.map +1 -0
- package/dist/react/hooks.d.ts +66 -0
- package/dist/react/hooks.d.ts.map +1 -0
- package/dist/react/hooks.js +121 -0
- package/dist/react/hooks.js.map +1 -0
- package/dist/react/index.d.ts +11 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +11 -0
- package/dist/react/index.js.map +1 -0
- package/dist/session/appReadyStore.d.ts +10 -0
- package/dist/session/appReadyStore.d.ts.map +1 -0
- package/dist/session/appReadyStore.js +22 -0
- package/dist/session/appReadyStore.js.map +1 -0
- package/dist/session/authStore.d.ts +71 -0
- package/dist/session/authStore.d.ts.map +1 -0
- package/dist/session/authStore.js +347 -0
- package/dist/session/authStore.js.map +1 -0
- package/dist/session/index.d.ts +10 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +10 -0
- package/dist/session/index.js.map +1 -0
- package/dist/storage/entityStore.d.ts +51 -0
- package/dist/storage/entityStore.d.ts.map +1 -0
- package/dist/storage/entityStore.js +78 -0
- package/dist/storage/entityStore.js.map +1 -0
- package/dist/storage/localStore.d.ts +144 -0
- package/dist/storage/localStore.d.ts.map +1 -0
- package/dist/storage/localStore.js +289 -0
- package/dist/storage/localStore.js.map +1 -0
- package/dist/storage/rehydrate.d.ts +56 -0
- package/dist/storage/rehydrate.d.ts.map +1 -0
- package/dist/storage/rehydrate.js +87 -0
- package/dist/storage/rehydrate.js.map +1 -0
- package/dist/storage/rehydrate.test.d.ts +2 -0
- package/dist/storage/rehydrate.test.d.ts.map +1 -0
- package/dist/storage/rehydrate.test.js +172 -0
- package/dist/storage/rehydrate.test.js.map +1 -0
- package/dist/storage/storageManager.d.ts +39 -0
- package/dist/storage/storageManager.d.ts.map +1 -0
- package/dist/storage/storageManager.js +68 -0
- package/dist/storage/storageManager.js.map +1 -0
- package/dist/storage/syncController.d.ts +79 -0
- package/dist/storage/syncController.d.ts.map +1 -0
- package/dist/storage/syncController.js +189 -0
- package/dist/storage/syncController.js.map +1 -0
- package/dist/storage/syncStatusStore.d.ts +17 -0
- package/dist/storage/syncStatusStore.d.ts.map +1 -0
- package/dist/storage/syncStatusStore.js +19 -0
- package/dist/storage/syncStatusStore.js.map +1 -0
- package/dist/storage/wasRemoteStore.d.ts +73 -0
- package/dist/storage/wasRemoteStore.d.ts.map +1 -0
- package/dist/storage/wasRemoteStore.js +76 -0
- package/dist/storage/wasRemoteStore.js.map +1 -0
- package/dist/storage/wasSync.d.ts +42 -0
- package/dist/storage/wasSync.d.ts.map +1 -0
- package/dist/storage/wasSync.js +34 -0
- package/dist/storage/wasSync.js.map +1 -0
- package/dist/sync/changesQuery.d.ts +44 -0
- package/dist/sync/changesQuery.d.ts.map +1 -0
- package/dist/sync/changesQuery.js +61 -0
- package/dist/sync/changesQuery.js.map +1 -0
- package/dist/sync/changesQuery.test.d.ts +2 -0
- package/dist/sync/changesQuery.test.d.ts.map +1 -0
- package/dist/sync/changesQuery.test.js +145 -0
- package/dist/sync/changesQuery.test.js.map +1 -0
- package/dist/sync/docCipher.d.ts +68 -0
- package/dist/sync/docCipher.d.ts.map +1 -0
- package/dist/sync/docCipher.js +89 -0
- package/dist/sync/docCipher.js.map +1 -0
- package/dist/sync/feedMasterPort.d.ts +30 -0
- package/dist/sync/feedMasterPort.d.ts.map +1 -0
- package/dist/sync/feedMasterPort.js +58 -0
- package/dist/sync/feedMasterPort.js.map +1 -0
- package/dist/sync/index.d.ts +21 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +21 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/lww.d.ts +29 -0
- package/dist/sync/lww.d.ts.map +1 -0
- package/dist/sync/lww.js +28 -0
- package/dist/sync/lww.js.map +1 -0
- package/dist/sync/lww.test.d.ts +2 -0
- package/dist/sync/lww.test.d.ts.map +1 -0
- package/dist/sync/lww.test.js +28 -0
- package/dist/sync/lww.test.js.map +1 -0
- package/dist/sync/lwwConflictHandler.d.ts +49 -0
- package/dist/sync/lwwConflictHandler.d.ts.map +1 -0
- package/dist/sync/lwwConflictHandler.js +65 -0
- package/dist/sync/lwwConflictHandler.js.map +1 -0
- package/dist/sync/lwwConflictHandler.test.d.ts +2 -0
- package/dist/sync/lwwConflictHandler.test.d.ts.map +1 -0
- package/dist/sync/lwwConflictHandler.test.js +78 -0
- package/dist/sync/lwwConflictHandler.test.js.map +1 -0
- package/dist/sync/pushWrites.d.ts +57 -0
- package/dist/sync/pushWrites.d.ts.map +1 -0
- package/dist/sync/pushWrites.js +159 -0
- package/dist/sync/pushWrites.js.map +1 -0
- package/dist/sync/pushWrites.test.d.ts +2 -0
- package/dist/sync/pushWrites.test.d.ts.map +1 -0
- package/dist/sync/pushWrites.test.js +287 -0
- package/dist/sync/pushWrites.test.js.map +1 -0
- package/dist/sync/syncedDocSchema.d.ts +22 -0
- package/dist/sync/syncedDocSchema.d.ts.map +1 -0
- package/dist/sync/syncedDocSchema.js +26 -0
- package/dist/sync/syncedDocSchema.js.map +1 -0
- package/dist/sync/types.d.ts +194 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/sync/types.js +36 -0
- package/dist/sync/types.js.map +1 -0
- package/dist/sync/wasReplication.d.ts +47 -0
- package/dist/sync/wasReplication.d.ts.map +1 -0
- package/dist/sync/wasReplication.js +55 -0
- package/dist/sync/wasReplication.js.map +1 -0
- package/dist/sync/wasSyncPort.d.ts +45 -0
- package/dist/sync/wasSyncPort.d.ts.map +1 -0
- package/dist/sync/wasSyncPort.js +170 -0
- package/dist/sync/wasSyncPort.js.map +1 -0
- package/package.json +142 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* App-session identity bootstrap: everything derivable from the master seed in
|
|
6
|
+
* one call. Thin composition over the pinned derivation in `agents.ts` (which
|
|
7
|
+
* is the shared-key contract: `CapabilityAgent.fromSeed` on raw bytes for the
|
|
8
|
+
* identity, HKDF `info = 'kak:v1:<collectionId>'` for each collection's KAK --
|
|
9
|
+
* never change either without a migration plan).
|
|
10
|
+
*
|
|
11
|
+
* The per-collection KAKs themselves are derived where they are consumed (the
|
|
12
|
+
* local store calls `deriveCollectionKeys` per collection); this module only
|
|
13
|
+
* surfaces the identity/zcap side the auth flow needs.
|
|
14
|
+
*/
|
|
15
|
+
import { type IdentityAgents } from './agents.js';
|
|
16
|
+
/**
|
|
17
|
+
* Derives the app's identity agents (stable did:key controller, signer,
|
|
18
|
+
* ZcapClient) from the master seed.
|
|
19
|
+
*
|
|
20
|
+
* @param options {object}
|
|
21
|
+
* @param options.seed {Uint8Array} the 32-byte master seed
|
|
22
|
+
* @returns {Promise<IdentityAgents>}
|
|
23
|
+
*/
|
|
24
|
+
export declare function initAppSession({ seed }: {
|
|
25
|
+
seed: Uint8Array;
|
|
26
|
+
}): Promise<IdentityAgents>;
|
|
27
|
+
//# sourceMappingURL=initAppSession.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"initAppSession.d.ts","sourceRoot":"","sources":["../../src/identity/initAppSession.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;GAUG;AACH,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAA;AAEjE;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAAC,EACnC,IAAI,EACL,EAAE;IACD,IAAI,EAAE,UAAU,CAAA;CACjB,GAAG,OAAO,CAAC,cAAc,CAAC,CAK1B"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* App-session identity bootstrap: everything derivable from the master seed in
|
|
6
|
+
* one call. Thin composition over the pinned derivation in `agents.ts` (which
|
|
7
|
+
* is the shared-key contract: `CapabilityAgent.fromSeed` on raw bytes for the
|
|
8
|
+
* identity, HKDF `info = 'kak:v1:<collectionId>'` for each collection's KAK --
|
|
9
|
+
* never change either without a migration plan).
|
|
10
|
+
*
|
|
11
|
+
* The per-collection KAKs themselves are derived where they are consumed (the
|
|
12
|
+
* local store calls `deriveCollectionKeys` per collection); this module only
|
|
13
|
+
* surfaces the identity/zcap side the auth flow needs.
|
|
14
|
+
*/
|
|
15
|
+
import { deriveIdentity } from './agents.js';
|
|
16
|
+
/**
|
|
17
|
+
* Derives the app's identity agents (stable did:key controller, signer,
|
|
18
|
+
* ZcapClient) from the master seed.
|
|
19
|
+
*
|
|
20
|
+
* @param options {object}
|
|
21
|
+
* @param options.seed {Uint8Array} the 32-byte master seed
|
|
22
|
+
* @returns {Promise<IdentityAgents>}
|
|
23
|
+
*/
|
|
24
|
+
export async function initAppSession({ seed }) {
|
|
25
|
+
if (seed.length !== 32) {
|
|
26
|
+
throw new Error(`Master seed must be 32 bytes (got ${seed.length}).`);
|
|
27
|
+
}
|
|
28
|
+
return await deriveIdentity({ seed });
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=initAppSession.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"initAppSession.js","sourceRoot":"","sources":["../../src/identity/initAppSession.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;GAUG;AACH,OAAO,EAAE,cAAc,EAAuB,MAAM,aAAa,CAAA;AAEjE;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,EACnC,IAAI,EAGL;IACC,IAAI,IAAI,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,qCAAqC,IAAI,CAAC,MAAM,IAAI,CAAC,CAAA;IACvE,CAAC;IACD,OAAO,MAAM,cAAc,CAAC,EAAE,IAAI,EAAE,CAAC,CAAA;AACvC,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { IVerifiableCredential, IVerifiablePresentation } from '@interop/data-integrity-core';
|
|
2
|
+
import type { DocumentLoader } from './documentLoader.js';
|
|
3
|
+
/**
|
|
4
|
+
* App-supplied naming for the seed credential: the VC `type` name and the URN
|
|
5
|
+
* vocabulary namespace its inline-context terms are minted under (e.g.
|
|
6
|
+
* `credentialType: 'MyAppKey'`, `vocabBase: 'urn:my-app:vocab#'`).
|
|
7
|
+
*/
|
|
8
|
+
export interface SeedCredentialConfig {
|
|
9
|
+
credentialType: string;
|
|
10
|
+
vocabBase: string;
|
|
11
|
+
}
|
|
12
|
+
/** A parsed and structurally validated seed credential. */
|
|
13
|
+
export interface ParsedSeedCredential {
|
|
14
|
+
seed: Uint8Array;
|
|
15
|
+
controllerDid: string;
|
|
16
|
+
}
|
|
17
|
+
/** Encodes bytes as base64url (no padding), browser- and Node-safe. */
|
|
18
|
+
export declare function bytesToBase64url(bytes: Uint8Array): string;
|
|
19
|
+
/** Decodes base64url text back into bytes. */
|
|
20
|
+
export declare function base64urlToBytes(text: string): Uint8Array;
|
|
21
|
+
/**
|
|
22
|
+
* Self-issues the app-key credential for `seed`, signed Ed25519Signature2020 by
|
|
23
|
+
* the seed-derived signer.
|
|
24
|
+
*
|
|
25
|
+
* @param options {object}
|
|
26
|
+
* @param options.seed {Uint8Array} the 32-byte master seed
|
|
27
|
+
* @param options.origin {string} this app's web origin (anti-phishing bind)
|
|
28
|
+
* @param options.config {SeedCredentialConfig} credential type + vocab
|
|
29
|
+
* @param options.documentLoader {DocumentLoader}
|
|
30
|
+
* @returns {Promise<IVerifiableCredential>}
|
|
31
|
+
*/
|
|
32
|
+
export declare function issueSeedCredential({ seed, origin, config, documentLoader }: {
|
|
33
|
+
seed: Uint8Array;
|
|
34
|
+
origin: string;
|
|
35
|
+
config: SeedCredentialConfig;
|
|
36
|
+
documentLoader: DocumentLoader;
|
|
37
|
+
}): Promise<IVerifiableCredential>;
|
|
38
|
+
/**
|
|
39
|
+
* Wraps a credential in a minimal (unsigned) VP for CHAPI `store()`. The
|
|
40
|
+
* credential's own proof self-authenticates; the offer envelope needs none.
|
|
41
|
+
*/
|
|
42
|
+
export declare function wrapCredentialForStore(credential: IVerifiableCredential): IVerifiablePresentation;
|
|
43
|
+
/**
|
|
44
|
+
* Parses a seed credential and enforces the structural contract: type,
|
|
45
|
+
* self-issue (issuer === subject id), origin binding, a well-formed 32-byte
|
|
46
|
+
* seed, and -- the strongest check -- that the DID derived from the embedded
|
|
47
|
+
* seed IS the credential's subject/issuer DID. (The cryptographic proof on the
|
|
48
|
+
* credential is verified separately at the presentation level.)
|
|
49
|
+
*
|
|
50
|
+
* @param options {object}
|
|
51
|
+
* @param options.credential {IVerifiableCredential}
|
|
52
|
+
* @param options.origin {string} the expected app origin
|
|
53
|
+
* @param options.config {SeedCredentialConfig} credential type + vocab
|
|
54
|
+
* @returns {Promise<ParsedSeedCredential>}
|
|
55
|
+
*/
|
|
56
|
+
export declare function parseSeedCredential({ credential, origin, config }: {
|
|
57
|
+
credential: IVerifiableCredential;
|
|
58
|
+
origin: string;
|
|
59
|
+
config: SeedCredentialConfig;
|
|
60
|
+
}): Promise<ParsedSeedCredential>;
|
|
61
|
+
/**
|
|
62
|
+
* Finds the seed credential inside a wallet response VP, or `null` when the
|
|
63
|
+
* wallet returned none (the first-run signal).
|
|
64
|
+
*
|
|
65
|
+
* @param options {object}
|
|
66
|
+
* @param options.presentation {IVerifiablePresentation}
|
|
67
|
+
* @param options.credentialType {string} the app's seed-credential type name
|
|
68
|
+
* @returns {IVerifiableCredential | null}
|
|
69
|
+
*/
|
|
70
|
+
export declare function findSeedCredential({ presentation, credentialType }: {
|
|
71
|
+
presentation: IVerifiablePresentation;
|
|
72
|
+
credentialType: string;
|
|
73
|
+
}): IVerifiableCredential | null;
|
|
74
|
+
//# sourceMappingURL=seedCredential.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seedCredential.d.ts","sourceRoot":"","sources":["../../src/identity/seedCredential.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EACV,qBAAqB,EACrB,uBAAuB,EACxB,MAAM,8BAA8B,CAAA;AAErC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAIzD;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,2DAA2D;AAC3D,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,UAAU,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;CACtB;AAmBD,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAM1D;AAED,8CAA8C;AAC9C,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CASzD;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CAAC,EACxC,IAAI,EACJ,MAAM,EACN,MAAM,EACN,cAAc,EACf,EAAE;IACD,IAAI,EAAE,UAAU,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,oBAAoB,CAAA;IAC5B,cAAc,EAAE,cAAc,CAAA;CAC/B,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAsBjC;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,qBAAqB,GAChC,uBAAuB,CAMzB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,mBAAmB,CAAC,EACxC,UAAU,EACV,MAAM,EACN,MAAM,EACP,EAAE;IACD,UAAU,EAAE,qBAAqB,CAAA;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,oBAAoB,CAAA;CAC7B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAyChC;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,YAAY,EACZ,cAAc,EACf,EAAE;IACD,YAAY,EAAE,uBAAuB,CAAA;IACrC,cAAc,EAAE,MAAM,CAAA;CACvB,GAAG,qBAAqB,GAAG,IAAI,CAY/B"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* The app-key credential: a self-issued VC holding the app's 32-byte master
|
|
6
|
+
* seed, stored in (and recovered from) the user's wallet. An inline-context
|
|
7
|
+
* pattern keeps it verifiable with no remote vocabulary fetch.
|
|
8
|
+
*
|
|
9
|
+
* The credential is self-issued: `issuer === credentialSubject.id`, and both
|
|
10
|
+
* equal the did:key controller DERIVED FROM THE EMBEDDED SEED -- so possession
|
|
11
|
+
* of the credential is possession of the identity, and a parsed credential can
|
|
12
|
+
* be re-checked against its own seed. `credentialSubject.origin` binds the
|
|
13
|
+
* credential to this app's web origin (the anti-phishing guard checked at
|
|
14
|
+
* login). The credential type name and vocabulary namespace are app-supplied
|
|
15
|
+
* (`SeedCredentialConfig`) so different apps hold sibling credential types on
|
|
16
|
+
* the same pattern.
|
|
17
|
+
*/
|
|
18
|
+
import * as vc from '@interop/vc';
|
|
19
|
+
import { Ed25519Signature2020 } from '@interop/ed25519-signature';
|
|
20
|
+
import { deriveIdentity } from './agents.js';
|
|
21
|
+
const VC_1_CONTEXT_URL = 'https://www.w3.org/2018/credentials/v1';
|
|
22
|
+
/**
|
|
23
|
+
* Builds the inline context object binding the app's credential terms to its
|
|
24
|
+
* URN vocabulary namespace, so the credential stays verifiable with no remote
|
|
25
|
+
* vocabulary fetch.
|
|
26
|
+
*/
|
|
27
|
+
function seedContext({ credentialType, vocabBase }) {
|
|
28
|
+
return {
|
|
29
|
+
'@protected': true,
|
|
30
|
+
[credentialType]: `${vocabBase}${credentialType}`,
|
|
31
|
+
seed: `${vocabBase}seed`,
|
|
32
|
+
origin: `${vocabBase}origin`
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** Encodes bytes as base64url (no padding), browser- and Node-safe. */
|
|
36
|
+
export function bytesToBase64url(bytes) {
|
|
37
|
+
let binary = '';
|
|
38
|
+
for (const byte of bytes) {
|
|
39
|
+
binary += String.fromCharCode(byte);
|
|
40
|
+
}
|
|
41
|
+
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
42
|
+
}
|
|
43
|
+
/** Decodes base64url text back into bytes. */
|
|
44
|
+
export function base64urlToBytes(text) {
|
|
45
|
+
const base64 = text.replace(/-/g, '+').replace(/_/g, '/');
|
|
46
|
+
const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);
|
|
47
|
+
const binary = atob(padded);
|
|
48
|
+
const bytes = new Uint8Array(binary.length);
|
|
49
|
+
for (let i = 0; i < binary.length; i++) {
|
|
50
|
+
bytes[i] = binary.charCodeAt(i);
|
|
51
|
+
}
|
|
52
|
+
return bytes;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Self-issues the app-key credential for `seed`, signed Ed25519Signature2020 by
|
|
56
|
+
* the seed-derived signer.
|
|
57
|
+
*
|
|
58
|
+
* @param options {object}
|
|
59
|
+
* @param options.seed {Uint8Array} the 32-byte master seed
|
|
60
|
+
* @param options.origin {string} this app's web origin (anti-phishing bind)
|
|
61
|
+
* @param options.config {SeedCredentialConfig} credential type + vocab
|
|
62
|
+
* @param options.documentLoader {DocumentLoader}
|
|
63
|
+
* @returns {Promise<IVerifiableCredential>}
|
|
64
|
+
*/
|
|
65
|
+
export async function issueSeedCredential({ seed, origin, config, documentLoader }) {
|
|
66
|
+
if (seed.length !== 32) {
|
|
67
|
+
throw new Error(`Master seed must be 32 bytes (got ${seed.length}).`);
|
|
68
|
+
}
|
|
69
|
+
const { controllerDid, keyAgent } = await deriveIdentity({ seed });
|
|
70
|
+
const credential = {
|
|
71
|
+
'@context': [VC_1_CONTEXT_URL, seedContext(config)],
|
|
72
|
+
id: `urn:uuid:${crypto.randomUUID()}`,
|
|
73
|
+
type: ['VerifiableCredential', config.credentialType],
|
|
74
|
+
issuer: controllerDid,
|
|
75
|
+
credentialSubject: {
|
|
76
|
+
id: controllerDid,
|
|
77
|
+
seed: bytesToBase64url(seed),
|
|
78
|
+
origin
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const suite = new Ed25519Signature2020({ signer: keyAgent.getSigner() });
|
|
82
|
+
return (await vc.issue({
|
|
83
|
+
credential,
|
|
84
|
+
suite,
|
|
85
|
+
documentLoader
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Wraps a credential in a minimal (unsigned) VP for CHAPI `store()`. The
|
|
90
|
+
* credential's own proof self-authenticates; the offer envelope needs none.
|
|
91
|
+
*/
|
|
92
|
+
export function wrapCredentialForStore(credential) {
|
|
93
|
+
return {
|
|
94
|
+
'@context': [VC_1_CONTEXT_URL],
|
|
95
|
+
type: ['VerifiablePresentation'],
|
|
96
|
+
verifiableCredential: [credential]
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Parses a seed credential and enforces the structural contract: type,
|
|
101
|
+
* self-issue (issuer === subject id), origin binding, a well-formed 32-byte
|
|
102
|
+
* seed, and -- the strongest check -- that the DID derived from the embedded
|
|
103
|
+
* seed IS the credential's subject/issuer DID. (The cryptographic proof on the
|
|
104
|
+
* credential is verified separately at the presentation level.)
|
|
105
|
+
*
|
|
106
|
+
* @param options {object}
|
|
107
|
+
* @param options.credential {IVerifiableCredential}
|
|
108
|
+
* @param options.origin {string} the expected app origin
|
|
109
|
+
* @param options.config {SeedCredentialConfig} credential type + vocab
|
|
110
|
+
* @returns {Promise<ParsedSeedCredential>}
|
|
111
|
+
*/
|
|
112
|
+
export async function parseSeedCredential({ credential, origin, config }) {
|
|
113
|
+
const { credentialType } = config;
|
|
114
|
+
const types = Array.isArray(credential.type)
|
|
115
|
+
? credential.type
|
|
116
|
+
: [credential.type];
|
|
117
|
+
if (!types.includes(credentialType)) {
|
|
118
|
+
throw new Error(`Credential is not a ${credentialType} credential.`);
|
|
119
|
+
}
|
|
120
|
+
const issuer = typeof credential.issuer === 'string'
|
|
121
|
+
? credential.issuer
|
|
122
|
+
: credential.issuer?.id;
|
|
123
|
+
const subject = credential.credentialSubject;
|
|
124
|
+
if (!issuer || !subject?.id || issuer !== subject.id) {
|
|
125
|
+
throw new Error(`${credentialType} credential is not self-issued.`);
|
|
126
|
+
}
|
|
127
|
+
if (subject.origin !== origin) {
|
|
128
|
+
throw new Error(`${credentialType} origin "${subject.origin ?? ''}" does not match this app's origin "${origin}".`);
|
|
129
|
+
}
|
|
130
|
+
if (typeof subject.seed !== 'string' || subject.seed.length === 0) {
|
|
131
|
+
throw new Error(`${credentialType} credential carries no seed.`);
|
|
132
|
+
}
|
|
133
|
+
const seed = base64urlToBytes(subject.seed);
|
|
134
|
+
if (seed.length !== 32) {
|
|
135
|
+
throw new Error(`${credentialType} seed must decode to 32 bytes (got ${seed.length}).`);
|
|
136
|
+
}
|
|
137
|
+
const { controllerDid } = await deriveIdentity({ seed });
|
|
138
|
+
if (controllerDid !== subject.id) {
|
|
139
|
+
throw new Error(`${credentialType} seed does not derive the credential subject DID.`);
|
|
140
|
+
}
|
|
141
|
+
return { seed, controllerDid };
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Finds the seed credential inside a wallet response VP, or `null` when the
|
|
145
|
+
* wallet returned none (the first-run signal).
|
|
146
|
+
*
|
|
147
|
+
* @param options {object}
|
|
148
|
+
* @param options.presentation {IVerifiablePresentation}
|
|
149
|
+
* @param options.credentialType {string} the app's seed-credential type name
|
|
150
|
+
* @returns {IVerifiableCredential | null}
|
|
151
|
+
*/
|
|
152
|
+
export function findSeedCredential({ presentation, credentialType }) {
|
|
153
|
+
const embedded = presentation
|
|
154
|
+
.verifiableCredential;
|
|
155
|
+
const list = Array.isArray(embedded) ? embedded : embedded ? [embedded] : [];
|
|
156
|
+
for (const entry of list) {
|
|
157
|
+
const types = entry.type;
|
|
158
|
+
const asArray = Array.isArray(types) ? types : [types];
|
|
159
|
+
if (asArray.includes(credentialType)) {
|
|
160
|
+
return entry;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=seedCredential.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seedCredential.js","sourceRoot":"","sources":["../../src/identity/seedCredential.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seedCredential.test.d.ts","sourceRoot":"","sources":["../../src/identity/seedCredential.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Seed-credential tests: issue/parse round trip, the self-issue / origin /
|
|
6
|
+
* seed-to-DID structural contract, and cryptographic verifiability of the
|
|
7
|
+
* issued credential.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, expect, it } from 'vitest';
|
|
10
|
+
import { verifyCredential } from '@interop/verifier-core';
|
|
11
|
+
import { base64urlToBytes, bytesToBase64url, findSeedCredential, issueSeedCredential, parseSeedCredential, wrapCredentialForStore } from './seedCredential.js';
|
|
12
|
+
import { createDocumentLoader } from './documentLoader.js';
|
|
13
|
+
import { deriveIdentity } from './agents.js';
|
|
14
|
+
const ORIGIN = 'http://localhost:5173';
|
|
15
|
+
const CONFIG = {
|
|
16
|
+
credentialType: 'TestAppKey',
|
|
17
|
+
vocabBase: 'urn:test-app:vocab#'
|
|
18
|
+
};
|
|
19
|
+
const documentLoader = createDocumentLoader();
|
|
20
|
+
function randomSeed() {
|
|
21
|
+
return crypto.getRandomValues(new Uint8Array(32));
|
|
22
|
+
}
|
|
23
|
+
describe('base64url helpers', () => {
|
|
24
|
+
it('round-trips arbitrary bytes', () => {
|
|
25
|
+
const bytes = randomSeed();
|
|
26
|
+
expect(base64urlToBytes(bytesToBase64url(bytes))).toEqual(bytes);
|
|
27
|
+
});
|
|
28
|
+
it('produces no padding or unsafe characters', () => {
|
|
29
|
+
const encoded = bytesToBase64url(randomSeed());
|
|
30
|
+
expect(encoded).toMatch(/^[A-Za-z0-9_-]+$/);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe('issueSeedCredential', () => {
|
|
34
|
+
it('issues a self-issued app key bound to the seed-derived DID', async () => {
|
|
35
|
+
const seed = randomSeed();
|
|
36
|
+
const { controllerDid } = await deriveIdentity({ seed });
|
|
37
|
+
const credential = await issueSeedCredential({
|
|
38
|
+
seed,
|
|
39
|
+
origin: ORIGIN,
|
|
40
|
+
config: CONFIG,
|
|
41
|
+
documentLoader
|
|
42
|
+
});
|
|
43
|
+
expect(credential.type).toContain(CONFIG.credentialType);
|
|
44
|
+
expect(credential.issuer).toBe(controllerDid);
|
|
45
|
+
const subject = credential.credentialSubject;
|
|
46
|
+
expect(subject.id).toBe(controllerDid);
|
|
47
|
+
expect(subject.origin).toBe(ORIGIN);
|
|
48
|
+
expect(base64urlToBytes(subject.seed)).toEqual(seed);
|
|
49
|
+
expect(credential.proof).toBeDefined();
|
|
50
|
+
});
|
|
51
|
+
it('issues a cryptographically verifiable credential', async () => {
|
|
52
|
+
const credential = await issueSeedCredential({
|
|
53
|
+
seed: randomSeed(),
|
|
54
|
+
origin: ORIGIN,
|
|
55
|
+
config: CONFIG,
|
|
56
|
+
documentLoader
|
|
57
|
+
});
|
|
58
|
+
const result = await verifyCredential({
|
|
59
|
+
credential,
|
|
60
|
+
registries: [],
|
|
61
|
+
documentLoader
|
|
62
|
+
});
|
|
63
|
+
expect(result.verified).toBe(true);
|
|
64
|
+
});
|
|
65
|
+
it('rejects a seed that is not 32 bytes', async () => {
|
|
66
|
+
await expect(issueSeedCredential({
|
|
67
|
+
seed: new Uint8Array(16),
|
|
68
|
+
origin: ORIGIN,
|
|
69
|
+
config: CONFIG,
|
|
70
|
+
documentLoader
|
|
71
|
+
})).rejects.toThrow(/32 bytes/);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe('parseSeedCredential', () => {
|
|
75
|
+
it('round-trips the seed and controller DID', async () => {
|
|
76
|
+
const seed = randomSeed();
|
|
77
|
+
const { controllerDid } = await deriveIdentity({ seed });
|
|
78
|
+
const credential = await issueSeedCredential({
|
|
79
|
+
seed,
|
|
80
|
+
origin: ORIGIN,
|
|
81
|
+
config: CONFIG,
|
|
82
|
+
documentLoader
|
|
83
|
+
});
|
|
84
|
+
const parsed = await parseSeedCredential({
|
|
85
|
+
credential,
|
|
86
|
+
origin: ORIGIN,
|
|
87
|
+
config: CONFIG
|
|
88
|
+
});
|
|
89
|
+
expect(parsed.seed).toEqual(seed);
|
|
90
|
+
expect(parsed.controllerDid).toBe(controllerDid);
|
|
91
|
+
});
|
|
92
|
+
it('rejects an origin mismatch', async () => {
|
|
93
|
+
const credential = await issueSeedCredential({
|
|
94
|
+
seed: randomSeed(),
|
|
95
|
+
origin: 'https://evil.example',
|
|
96
|
+
config: CONFIG,
|
|
97
|
+
documentLoader
|
|
98
|
+
});
|
|
99
|
+
await expect(parseSeedCredential({ credential, origin: ORIGIN, config: CONFIG })).rejects.toThrow(/origin/);
|
|
100
|
+
});
|
|
101
|
+
it('rejects a non-self-issued credential', async () => {
|
|
102
|
+
const credential = await issueSeedCredential({
|
|
103
|
+
seed: randomSeed(),
|
|
104
|
+
origin: ORIGIN,
|
|
105
|
+
config: CONFIG,
|
|
106
|
+
documentLoader
|
|
107
|
+
});
|
|
108
|
+
const tampered = {
|
|
109
|
+
...credential,
|
|
110
|
+
issuer: 'did:key:z6MkfDbczcXk3XiivKp9kJvBGnBcyhrbsmLAjLgyDJnYCyj4'
|
|
111
|
+
};
|
|
112
|
+
await expect(parseSeedCredential({
|
|
113
|
+
credential: tampered,
|
|
114
|
+
origin: ORIGIN,
|
|
115
|
+
config: CONFIG
|
|
116
|
+
})).rejects.toThrow(/self-issued/);
|
|
117
|
+
});
|
|
118
|
+
it('rejects a credential whose seed does not derive its subject DID', async () => {
|
|
119
|
+
const credential = await issueSeedCredential({
|
|
120
|
+
seed: randomSeed(),
|
|
121
|
+
origin: ORIGIN,
|
|
122
|
+
config: CONFIG,
|
|
123
|
+
documentLoader
|
|
124
|
+
});
|
|
125
|
+
const subject = credential.credentialSubject;
|
|
126
|
+
const tampered = {
|
|
127
|
+
...credential,
|
|
128
|
+
credentialSubject: {
|
|
129
|
+
...subject,
|
|
130
|
+
seed: bytesToBase64url(randomSeed())
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
await expect(parseSeedCredential({
|
|
134
|
+
credential: tampered,
|
|
135
|
+
origin: ORIGIN,
|
|
136
|
+
config: CONFIG
|
|
137
|
+
})).rejects.toThrow(/does not derive/);
|
|
138
|
+
});
|
|
139
|
+
it('rejects the wrong credential type', async () => {
|
|
140
|
+
const credential = {
|
|
141
|
+
'@context': ['https://www.w3.org/2018/credentials/v1'],
|
|
142
|
+
type: ['VerifiableCredential'],
|
|
143
|
+
issuer: 'did:example:x',
|
|
144
|
+
credentialSubject: { id: 'did:example:x' }
|
|
145
|
+
};
|
|
146
|
+
await expect(parseSeedCredential({ credential, origin: ORIGIN, config: CONFIG })).rejects.toThrow(/not a TestAppKey/);
|
|
147
|
+
});
|
|
148
|
+
it('rejects a malformed seed', async () => {
|
|
149
|
+
const credential = await issueSeedCredential({
|
|
150
|
+
seed: randomSeed(),
|
|
151
|
+
origin: ORIGIN,
|
|
152
|
+
config: CONFIG,
|
|
153
|
+
documentLoader
|
|
154
|
+
});
|
|
155
|
+
const subject = credential.credentialSubject;
|
|
156
|
+
const tampered = {
|
|
157
|
+
...credential,
|
|
158
|
+
credentialSubject: {
|
|
159
|
+
...subject,
|
|
160
|
+
seed: bytesToBase64url(new Uint8Array(8))
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
await expect(parseSeedCredential({
|
|
164
|
+
credential: tampered,
|
|
165
|
+
origin: ORIGIN,
|
|
166
|
+
config: CONFIG
|
|
167
|
+
})).rejects.toThrow(/32 bytes/);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
describe('findSeedCredential / wrapCredentialForStore', () => {
|
|
171
|
+
it('finds the app key inside a VP and ignores other credentials', async () => {
|
|
172
|
+
const credential = await issueSeedCredential({
|
|
173
|
+
seed: randomSeed(),
|
|
174
|
+
origin: ORIGIN,
|
|
175
|
+
config: CONFIG,
|
|
176
|
+
documentLoader
|
|
177
|
+
});
|
|
178
|
+
const other = {
|
|
179
|
+
'@context': ['https://www.w3.org/2018/credentials/v1'],
|
|
180
|
+
type: ['VerifiableCredential'],
|
|
181
|
+
issuer: 'did:example:x',
|
|
182
|
+
credentialSubject: {}
|
|
183
|
+
};
|
|
184
|
+
const vp = wrapCredentialForStore(credential);
|
|
185
|
+
vp.verifiableCredential = [
|
|
186
|
+
other,
|
|
187
|
+
credential
|
|
188
|
+
];
|
|
189
|
+
expect(findSeedCredential({
|
|
190
|
+
presentation: vp,
|
|
191
|
+
credentialType: CONFIG.credentialType
|
|
192
|
+
})).toBe(credential);
|
|
193
|
+
});
|
|
194
|
+
it('returns null when the VP carries no app key (first-run signal)', () => {
|
|
195
|
+
const vp = {
|
|
196
|
+
'@context': ['https://www.w3.org/2018/credentials/v1'],
|
|
197
|
+
type: ['VerifiablePresentation']
|
|
198
|
+
};
|
|
199
|
+
expect(findSeedCredential({
|
|
200
|
+
presentation: vp,
|
|
201
|
+
credentialType: CONFIG.credentialType
|
|
202
|
+
})).toBeNull();
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
//# sourceMappingURL=seedCredential.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seedCredential.test.js","sourceRoot":"","sources":["../../src/identity/seedCredential.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;GAIG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAEzD,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,EAEvB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAE5C,MAAM,MAAM,GAAG,uBAAuB,CAAA;AACtC,MAAM,MAAM,GAAyB;IACnC,cAAc,EAAE,YAAY;IAC5B,SAAS,EAAE,qBAAqB;CACjC,CAAA;AACD,MAAM,cAAc,GAAG,oBAAoB,EAAE,CAAA;AAE7C,SAAS,UAAU;IACjB,OAAO,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAA;AACnD,CAAC;AAED,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,KAAK,GAAG,UAAU,EAAE,CAAA;QAC1B,MAAM,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAClE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,OAAO,GAAG,gBAAgB,CAAC,UAAU,EAAE,CAAC,CAAA;QAC9C,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAC7C,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;QAC1E,MAAM,IAAI,GAAG,UAAU,EAAE,CAAA;QACzB,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,cAAc,CAAC,EAAE,IAAI,EAAE,CAAC,CAAA;QACxD,MAAM,UAAU,GAAG,MAAM,mBAAmB,CAAC;YAC3C,IAAI;YACJ,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,MAAM;YACd,cAAc;SACf,CAAC,CAAA;QAEF,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,cAAc,CAAC,CAAA;QACxD,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;QAC7C,MAAM,OAAO,GAAG,UAAU,CAAC,iBAI1B,CAAA;QACD,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;QACtC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACnC,MAAM,CAAC,gBAAgB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QACpD,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAA;IACxC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QAChE,MAAM,UAAU,GAAG,MAAM,mBAAmB,CAAC;YAC3C,IAAI,EAAE,UAAU,EAAE;YAClB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,MAAM;YACd,cAAc;SACf,CAAC,CAAA;QACF,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC;YACpC,UAAU;YACV,UAAU,EAAE,EAAE;YACd,cAAc;SACf,CAAC,CAAA;QACF,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACpC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACnD,MAAM,MAAM,CACV,mBAAmB,CAAC;YAClB,IAAI,EAAE,IAAI,UAAU,CAAC,EAAE,CAAC;YACxB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,MAAM;YACd,cAAc;SACf,CAAC,CACH,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;IAC/B,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;QACvD,MAAM,IAAI,GAAG,UAAU,EAAE,CAAA;QACzB,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,cAAc,CAAC,EAAE,IAAI,EAAE,CAAC,CAAA;QACxD,MAAM,UAAU,GAAG,MAAM,mBAAmB,CAAC;YAC3C,IAAI;YACJ,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,MAAM;YACd,cAAc;SACf,CAAC,CAAA;QACF,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC;YACvC,UAAU;YACV,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,MAAM;SACf,CAAC,CAAA;QACF,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QACjC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;IAClD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;QAC1C,MAAM,UAAU,GAAG,MAAM,mBAAmB,CAAC;YAC3C,IAAI,EAAE,UAAU,EAAE;YAClB,MAAM,EAAE,sBAAsB;YAC9B,MAAM,EAAE,MAAM;YACd,cAAc;SACf,CAAC,CAAA;QACF,MAAM,MAAM,CACV,mBAAmB,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CACpE,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC7B,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACpD,MAAM,UAAU,GAAG,MAAM,mBAAmB,CAAC;YAC3C,IAAI,EAAE,UAAU,EAAE;YAClB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,MAAM;YACd,cAAc;SACf,CAAC,CAAA;QACF,MAAM,QAAQ,GAAG;YACf,GAAG,UAAU;YACb,MAAM,EAAE,0DAA0D;SAC1C,CAAA;QAC1B,MAAM,MAAM,CACV,mBAAmB,CAAC;YAClB,UAAU,EAAE,QAAQ;YACpB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,MAAM;SACf,CAAC,CACH,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAA;IAClC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,MAAM,UAAU,GAAG,MAAM,mBAAmB,CAAC;YAC3C,IAAI,EAAE,UAAU,EAAE;YAClB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,MAAM;YACd,cAAc;SACf,CAAC,CAAA;QACF,MAAM,OAAO,GAAG,UAAU,CAAC,iBAA4C,CAAA;QACvE,MAAM,QAAQ,GAAG;YACf,GAAG,UAAU;YACb,iBAAiB,EAAE;gBACjB,GAAG,OAAO;gBACV,IAAI,EAAE,gBAAgB,CAAC,UAAU,EAAE,CAAC;aACrC;SACuB,CAAA;QAC1B,MAAM,MAAM,CACV,mBAAmB,CAAC;YAClB,UAAU,EAAE,QAAQ;YACpB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,MAAM;SACf,CAAC,CACH,CAAC,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAA;IACtC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,UAAU,GAAG;YACjB,UAAU,EAAE,CAAC,wCAAwC,CAAC;YACtD,IAAI,EAAE,CAAC,sBAAsB,CAAC;YAC9B,MAAM,EAAE,eAAe;YACvB,iBAAiB,EAAE,EAAE,EAAE,EAAE,eAAe,EAAE;SACP,CAAA;QACrC,MAAM,MAAM,CACV,mBAAmB,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CACpE,CAAC,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAA;IACvC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;QACxC,MAAM,UAAU,GAAG,MAAM,mBAAmB,CAAC;YAC3C,IAAI,EAAE,UAAU,EAAE;YAClB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,MAAM;YACd,cAAc;SACf,CAAC,CAAA;QACF,MAAM,OAAO,GAAG,UAAU,CAAC,iBAA4C,CAAA;QACvE,MAAM,QAAQ,GAAG;YACf,GAAG,UAAU;YACb,iBAAiB,EAAE;gBACjB,GAAG,OAAO;gBACV,IAAI,EAAE,gBAAgB,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;aAC1C;SACuB,CAAA;QAC1B,MAAM,MAAM,CACV,mBAAmB,CAAC;YAClB,UAAU,EAAE,QAAQ;YACpB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,MAAM;SACf,CAAC,CACH,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;IAC/B,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,6CAA6C,EAAE,GAAG,EAAE;IAC3D,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,UAAU,GAAG,MAAM,mBAAmB,CAAC;YAC3C,IAAI,EAAE,UAAU,EAAE;YAClB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,MAAM;YACd,cAAc;SACf,CAAC,CAAA;QACF,MAAM,KAAK,GAAG;YACZ,UAAU,EAAE,CAAC,wCAAwC,CAAC;YACtD,IAAI,EAAE,CAAC,sBAAsB,CAAC;YAC9B,MAAM,EAAE,eAAe;YACvB,iBAAiB,EAAE,EAAE;SACtB,CAAA;QACD,MAAM,EAAE,GAAG,sBAAsB,CAAC,UAAU,CAAC,CAC5C;QAAC,EAA0C,CAAC,oBAAoB,GAAG;YAClE,KAAK;YACL,UAAU;SACX,CAAA;QACD,MAAM,CACJ,kBAAkB,CAAC;YACjB,YAAY,EAAE,EAAE;YAChB,cAAc,EAAE,MAAM,CAAC,cAAc;SACtC,CAAC,CACH,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IACpB,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACxE,MAAM,EAAE,GAAG;YACT,UAAU,EAAE,CAAC,wCAAwC,CAAC;YACtD,IAAI,EAAE,CAAC,wBAAwB,CAAC;SACxB,CAAA;QACV,MAAM,CACJ,kBAAkB,CAAC;YACjB,YAAY,EAAE,EAAE;YAChB,cAAc,EAAE,MAAM,CAAC,cAAc;SACtC,CAAC,CACH,CAAC,QAAQ,EAAE,CAAA;IACd,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** The bound seed-store operations returned by `createSeedStore`. */
|
|
2
|
+
export interface SeedStore {
|
|
3
|
+
/** Persists the 32-byte master seed. */
|
|
4
|
+
saveSeed(seed: Uint8Array): Promise<void>;
|
|
5
|
+
/** Loads the persisted master seed, or `null`. */
|
|
6
|
+
loadSeed(): Promise<Uint8Array | null>;
|
|
7
|
+
/** Persists an opaque session record (see `appSession.ts`). */
|
|
8
|
+
saveRecord(record: unknown): Promise<void>;
|
|
9
|
+
/** Loads the persisted session record, or `null`. */
|
|
10
|
+
loadRecord(): Promise<unknown | null>;
|
|
11
|
+
/** Wipes the seed and the session record (logout). */
|
|
12
|
+
clearSeedStore(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Creates a seed store bound to `dbName` and `idb`.
|
|
16
|
+
*
|
|
17
|
+
* @param options {object}
|
|
18
|
+
* @param options.dbName {string} the IndexedDB database name
|
|
19
|
+
* @param [options.idb] {IDBFactory} the IndexedDB factory (defaults to the
|
|
20
|
+
* global `indexedDB`; inject a fake for tests)
|
|
21
|
+
* @returns {SeedStore}
|
|
22
|
+
*/
|
|
23
|
+
export declare function createSeedStore({ dbName, idb }: {
|
|
24
|
+
dbName: string;
|
|
25
|
+
idb?: IDBFactory;
|
|
26
|
+
}): SeedStore;
|
|
27
|
+
//# sourceMappingURL=seedStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seedStore.d.ts","sourceRoot":"","sources":["../../src/identity/seedStore.ts"],"names":[],"mappings":"AAiBA,qEAAqE;AACrE,MAAM,WAAW,SAAS;IACxB,wCAAwC;IACxC,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzC,kDAAkD;IAClD,QAAQ,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAA;IACtC,+DAA+D;IAC/D,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1C,qDAAqD;IACrD,UAAU,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;IACrC,sDAAsD;IACtD,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAChC;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,GAAe,EAChB,EAAE;IACD,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,UAAU,CAAA;CACjB,GAAG,SAAS,CA2DZ"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Seed persistence: the master seed at rest in the app's own IndexedDB, so a
|
|
6
|
+
* reload restores the session with zero wallet popups. A raw-IndexedDB pattern
|
|
7
|
+
* (one db, one object store, fixed record keys, `db.close()` after every
|
|
8
|
+
* operation). Wiped on logout.
|
|
9
|
+
*
|
|
10
|
+
* `createSeedStore` binds a database name (each app supplies its own) and an
|
|
11
|
+
* optional `idb` factory (injectable for tests, e.g. fake-indexeddb), returning
|
|
12
|
+
* the five bound operations.
|
|
13
|
+
*/
|
|
14
|
+
const SESSION_STORE = 'session';
|
|
15
|
+
const SEED_RECORD = 'seed';
|
|
16
|
+
const SESSION_RECORD = 'record';
|
|
17
|
+
/**
|
|
18
|
+
* Creates a seed store bound to `dbName` and `idb`.
|
|
19
|
+
*
|
|
20
|
+
* @param options {object}
|
|
21
|
+
* @param options.dbName {string} the IndexedDB database name
|
|
22
|
+
* @param [options.idb] {IDBFactory} the IndexedDB factory (defaults to the
|
|
23
|
+
* global `indexedDB`; inject a fake for tests)
|
|
24
|
+
* @returns {SeedStore}
|
|
25
|
+
*/
|
|
26
|
+
export function createSeedStore({ dbName, idb = indexedDB }) {
|
|
27
|
+
async function openSessionDb() {
|
|
28
|
+
return await new Promise((resolve, reject) => {
|
|
29
|
+
const request = idb.open(dbName, 1);
|
|
30
|
+
request.onupgradeneeded = () => {
|
|
31
|
+
request.result.createObjectStore(SESSION_STORE);
|
|
32
|
+
};
|
|
33
|
+
request.onsuccess = () => resolve(request.result);
|
|
34
|
+
request.onerror = () => reject(request.error ?? new Error('IndexedDB open failed.'));
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
async function withSessionStore(mode, operation) {
|
|
38
|
+
const db = await openSessionDb();
|
|
39
|
+
try {
|
|
40
|
+
return await new Promise((resolve, reject) => {
|
|
41
|
+
const transaction = db.transaction(SESSION_STORE, mode);
|
|
42
|
+
const request = operation(transaction.objectStore(SESSION_STORE));
|
|
43
|
+
request.onsuccess = () => resolve(request.result);
|
|
44
|
+
request.onerror = () => reject(request.error ?? new Error('IndexedDB operation failed.'));
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
db.close();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
async saveSeed(seed) {
|
|
53
|
+
await withSessionStore('readwrite', store => store.put(seed, SEED_RECORD));
|
|
54
|
+
},
|
|
55
|
+
async loadSeed() {
|
|
56
|
+
const stored = await withSessionStore('readonly', store => store.get(SEED_RECORD));
|
|
57
|
+
return stored instanceof Uint8Array && stored.length === 32
|
|
58
|
+
? stored
|
|
59
|
+
: null;
|
|
60
|
+
},
|
|
61
|
+
async saveRecord(record) {
|
|
62
|
+
await withSessionStore('readwrite', store => store.put(record, SESSION_RECORD));
|
|
63
|
+
},
|
|
64
|
+
async loadRecord() {
|
|
65
|
+
const stored = await withSessionStore('readonly', store => store.get(SESSION_RECORD));
|
|
66
|
+
return stored ?? null;
|
|
67
|
+
},
|
|
68
|
+
async clearSeedStore() {
|
|
69
|
+
await withSessionStore('readwrite', store => store.delete(SEED_RECORD));
|
|
70
|
+
await withSessionStore('readwrite', store => store.delete(SESSION_RECORD));
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=seedStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seedStore.js","sourceRoot":"","sources":["../../src/identity/seedStore.ts"],"names":[],"mappings":"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"}
|