@interop/was-react 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -379,38 +379,44 @@ The MUI entry supplies a router gate and status UI on top of this; see
379
379
 
380
380
  ## Login flow
381
381
 
382
- Login is driven by CHAPI Verifiable Presentation Requests (VPRs). The
383
- `useLogin().login()` action (backed by `loginWithWallet`) runs the flow, with a
384
- `phase` string surfaced for a progress line (`probing` to `storing-key` to
385
- `requesting-grants` to `verifying`):
382
+ Login is a single CHAPI exchange -- App Connect. The `useLogin().login()` action
383
+ (backed by `loginWithWallet`) runs the flow, with a `phase` string surfaced for
384
+ a progress line (`connecting` to `verifying`):
386
385
 
387
386
  1. **CHAPI polyfill loads lazily.** The `credential-handler-polyfill` is loaded
388
387
  on demand the first time a wallet request is made, not at import time.
389
- 2. **Probe (popup #1).** A VPR asking for DIDAuthentication plus the app's seed
390
- credential is sent to the wallet. No credential returned means this is a
391
- first run.
392
- 3. **First run: mint and store the seed.** A fresh 32-byte master seed is
393
- generated (`crypto.getRandomValues`), a seed credential is self-issued
394
- (`issueSeedCredential`, using the configured `credentialType` and `vocabBase`
395
- plus the app `origin` anti-phishing bind), and stored in the wallet via
396
- `chapiStore`. The flow blocks until the wallet confirms the store -- a
397
- dismissed store would silently break cross-device recovery.
398
- 4. **Returning login: recover and verify.** When the probe returns the seed
399
- credential, `parseSeedCredential` recovers the seed and cryptographically
400
- verifies the credential carries the shared `AppKeyCredential` marker type and
401
- is self-issued, origin-bound, and seed-to-DID bound.
402
- 5. **Derive identity.** The stable `did:key` controller and its signer are
388
+ 2. **One popup.** A VPR carrying a `DIDAuthentication` query plus one
389
+ `AppConnectQuery` -- the app's display name, `credentialType`, and
390
+ `vocabBase`, a `capabilityQuery` entry per configured collection, and a
391
+ `urn:was:shared-collection` entry per shared collection -- is sent via
392
+ `navigator.credentials.get` (`buildAppConnectVpr`). A `null` response is a
393
+ user cancel (`LoginCancelledError`), not an error.
394
+ 3. **The wallet matches or mints.** On a first run the wallet generates the
395
+ 32-byte seed itself, self-issues the origin-bound app-key credential, and
396
+ stores it in its own credential store under the same consent; on a returning
397
+ visit it matches the stored credential by the shared `AppKeyCredential`
398
+ marker type, the app's `credentialType`, the requesting origin, and the
399
+ seed-to-subject binding. The app never mints, and there is no second popup.
400
+ 4. **Verify the response.** `verifyLoginPresentation` checks the VP and embedded
401
+ proofs (purpose `authentication`, matching domain and challenge). A
402
+ presentation that verifies but carries no seed credential means the wallet
403
+ predates App Connect: the flow fails closed with `WalletUnsupportedError`
404
+ rather than degrading into a partial generic flow.
405
+ 5. **Recover the seed.** `parseSeedCredential` verifies the returned credential
406
+ carries the `AppKeyCredential` marker type and is self-issued, origin-bound,
407
+ and seed-to-DID bound, then recovers the seed.
408
+ 6. **Derive identity.** The stable `did:key` controller and its signer are
403
409
  derived deterministically from the seed via `CapabilityAgent.fromSeed`
404
410
  (`initAppSession` / `deriveIdentity`). The same seed yields the same identity
405
411
  on every device.
406
- 6. **Request grants (popup #2).** `buildGrantsVpr` requests a per-collection
407
- read/write zcap (plus a read-only space grant) for the controller DID. The
408
- wallet provisions the collections and returns a signed presentation.
409
- 7. **Verify the response.** `verifyLoginPresentation` checks the VP and embedded
410
- proofs (purpose `authentication`, matching domain and challenge), and
411
- `checkGrants` asserts every zcap is controlled by the app DID, shares one
412
- space on a single WAS host, and is unexpired. The wallet decides where the
413
- user's Space lives; the sync layer derives its target from the grants.
412
+ 7. **Check grants.** The delegated zcaps ride in the same presentation's
413
+ top-level `zcap` array. `checkGrants` asserts every zcap is controlled by the
414
+ app DID, shares one space on a single WAS host, covers each configured
415
+ collection's required actions, and is unexpired. A declined shared-collection
416
+ grant never fails the login. The wallet decides where the user's Space lives;
417
+ the sync layer derives its target from the grants. The wallet-provided
418
+ `appConnect: { firstRun }` member surfaces as the `{ firstRun }` the
419
+ `login()` promise resolves with.
414
420
  8. **Activate.** The session (seed, grants, earliest expiry) is persisted to
415
421
  IndexedDB, the encrypted local store is opened, the entity stores hydrate,
416
422
  and background WAS sync starts.
@@ -31,8 +31,8 @@
31
31
  *
32
32
  * Earlier versions used a second identity for app-provisioned collections: a
33
33
  * per-collection KAK, HKDF-derived from the master seed under the collection id
34
- * (`deriveCollectionKeys`, label `kak:v1:<collectionId>`). That derivation still
35
- * lives in `@interop/wallet-core/identity` but has no caller here. Unifying cost
34
+ * (`deriveCollectionKeys`, label `kak:v1:<collectionId>`). That derivation has
35
+ * been removed from `@interop/wallet-core/identity`. Unifying cost
36
36
  * the HKDF domain separation between collections -- the identity KAK now reads
37
37
  * every collection the app touches. In the multi-recipient model that key only
38
38
  * unwraps an epoch secret rather than being the content key, and the seed it was
@@ -31,8 +31,8 @@
31
31
  *
32
32
  * Earlier versions used a second identity for app-provisioned collections: a
33
33
  * per-collection KAK, HKDF-derived from the master seed under the collection id
34
- * (`deriveCollectionKeys`, label `kak:v1:<collectionId>`). That derivation still
35
- * lives in `@interop/wallet-core/identity` but has no caller here. Unifying cost
34
+ * (`deriveCollectionKeys`, label `kak:v1:<collectionId>`). That derivation has
35
+ * been removed from `@interop/wallet-core/identity`. Unifying cost
36
36
  * the HKDF domain separation between collections -- the identity KAK now reads
37
37
  * every collection the app touches. In the multi-recipient model that key only
38
38
  * unwraps an epoch secret rather than being the content key, and the seed it was
@@ -1 +1 @@
1
- {"version":3,"file":"documentLoader.d.ts","sourceRoot":"","sources":["../../src/identity/documentLoader.ts"],"names":[],"mappings":"AAYA;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACpD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,QAAQ,EAAE,OAAO,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;CACpB,CAAC,CAAA;AAIF;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,cAAc,CAErD"}
1
+ {"version":3,"file":"documentLoader.d.ts","sourceRoot":"","sources":["../../src/identity/documentLoader.ts"],"names":[],"mappings":"AAsBA;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACpD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,QAAQ,EAAE,OAAO,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;CACpB,CAAC,CAAA;AAeF;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,cAAc,CAErD"}
@@ -3,13 +3,28 @@
3
3
  */
4
4
  /**
5
5
  * The app's one JSON-LD document loader: static security contexts plus did:key
6
- * / did:web resolution (`@interop/security-document-loader`). did:web DIDs on
7
- * loopback hosts (a local dev WAS server on `localhost` or `127.0.0.1`)
8
- * resolve over plain http; the resolver handles that natively, so no dev shim
9
- * is needed here.
6
+ * / did:web / did:webvh resolution. The resolver starts from
7
+ * `@interop/security-document-loader`'s default did:key + did:web driver set
8
+ * and adds the `@interop/did-method-webvh` driver, so a wallet may present its
9
+ * VP holder as a did:webvh (signed with a `<did:webvh>#<key>` verification
10
+ * method) and verification still resolves it. webvh resolution stays VERIFIED
11
+ * resolution: the driver's default history-log verifier (hash chain + entry
12
+ * proofs) is active, so a tampered `did.jsonl` fails closed. DIDs on loopback
13
+ * hosts (a local dev server) resolve over plain http; both the did:web
14
+ * resolver and the webvh driver's `did.jsonl` fetch handle that natively, so
15
+ * no dev shim is needed here.
10
16
  */
11
- import { securityLoader } from '@interop/security-document-loader';
12
- const baseLoader = securityLoader({ fetchRemoteContexts: true }).build();
17
+ import { createDidWebvhDriver } from '@interop/did-method-webvh/driver';
18
+ import { createDefaultDidResolver, securityLoader } from '@interop/security-document-loader';
19
+ const didResolver = createDefaultDidResolver();
20
+ // The webvh driver is resolution-only (`{ method, get, resolveDID }`) by
21
+ // design; did-io's DidMethodDriver type also demands the key-generation
22
+ // surface (generate, fromKeyPair, ...), which resolution never calls.
23
+ didResolver.use(createDidWebvhDriver());
24
+ const baseLoader = securityLoader({
25
+ fetchRemoteContexts: true,
26
+ didResolver
27
+ }).build();
13
28
  /**
14
29
  * Builds the JSON-LD document loader handed to `@interop/vc` issuance and
15
30
  * verifier-core verification.
@@ -1 +1 @@
1
- {"version":3,"file":"documentLoader.js","sourceRoot":"","sources":["../../src/identity/documentLoader.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;GAMG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAA;AAWlE,MAAM,UAAU,GAAG,cAAc,CAAC,EAAE,mBAAmB,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,CAAA;AAExE;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,UAA4B,CAAA;AACrC,CAAC"}
1
+ {"version":3,"file":"documentLoader.js","sourceRoot":"","sources":["../../src/identity/documentLoader.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAA;AACvE,OAAO,EACL,wBAAwB,EACxB,cAAc,EACf,MAAM,mCAAmC,CAAA;AAW1C,MAAM,WAAW,GAAG,wBAAwB,EAAE,CAAA;AAC9C,yEAAyE;AACzE,wEAAwE;AACxE,sEAAsE;AACtE,WAAW,CAAC,GAAG,CACb,oBAAoB,EAAsD,CAC3E,CAAA;AAED,MAAM,UAAU,GAAG,cAAc,CAAC;IAChC,mBAAmB,EAAE,IAAI;IACzB,WAAW;CACZ,CAAC,CAAC,KAAK,EAAE,CAAA;AAEV;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,UAA4B,CAAA;AACrC,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@interop/was-react",
3
3
  "description": "React library for building 'Bring Your Own Everything' (BYOE) apps on Wallet Attached Storage: DID Auth login via CHAPI wallet, local-first encrypted storage, and background sync to a WAS server.",
4
- "version": "0.5.0",
4
+ "version": "0.6.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "build": "pnpm run clear && tsc",
@@ -47,13 +47,14 @@
47
47
  "sideEffects": false,
48
48
  "dependencies": {
49
49
  "@interop/data-integrity-core": "^8.4.0",
50
+ "@interop/did-method-webvh": "^5.0.0",
50
51
  "@interop/ed25519-signature": "^7.1.4",
51
52
  "@interop/ezcap": "^7.4.1",
52
- "@interop/security-document-loader": "^9.4.4",
53
+ "@interop/security-document-loader": "^9.5.0",
53
54
  "@interop/vc": "^11.0.6",
54
55
  "@interop/verifier-core": "^3.5.0",
55
56
  "@interop/wallet-core": "^0.5.0",
56
- "@interop/was-client": "^0.20.0",
57
+ "@interop/was-client": "^0.22.0",
57
58
  "@interop/webkms-client": "^14.7.1",
58
59
  "@interop/x25519-key-agreement-key": "^5.2.1",
59
60
  "@scure/base": "^2.2.0",