@interop/was-react 0.8.3 → 0.10.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.
Files changed (149) hide show
  1. package/README.md +50 -44
  2. package/dist/auth/chapi.d.ts.map +1 -1
  3. package/dist/auth/chapi.js +5 -4
  4. package/dist/auth/chapi.js.map +1 -1
  5. package/dist/auth/loginFlow.d.ts.map +1 -1
  6. package/dist/auth/loginFlow.js +48 -50
  7. package/dist/auth/loginFlow.js.map +1 -1
  8. package/dist/auth/loginRequest.d.ts +38 -5
  9. package/dist/auth/loginRequest.d.ts.map +1 -1
  10. package/dist/auth/loginRequest.js +66 -5
  11. package/dist/auth/loginRequest.js.map +1 -1
  12. package/dist/auth/verifyResponse.d.ts +7 -4
  13. package/dist/auth/verifyResponse.d.ts.map +1 -1
  14. package/dist/auth/verifyResponse.js +23 -13
  15. package/dist/auth/verifyResponse.js.map +1 -1
  16. package/dist/auth/walletRequestTypes.d.ts +9 -90
  17. package/dist/auth/walletRequestTypes.d.ts.map +1 -1
  18. package/dist/config.d.ts +9 -9
  19. package/dist/config.js +0 -0
  20. package/dist/dev/provisionDevGrants.d.ts.map +1 -1
  21. package/dist/dev/provisionDevGrants.js +18 -19
  22. package/dist/dev/provisionDevGrants.js.map +1 -1
  23. package/dist/grants.d.ts +19 -9
  24. package/dist/grants.d.ts.map +1 -1
  25. package/dist/grants.js +17 -2
  26. package/dist/grants.js.map +1 -1
  27. package/dist/identity/agents.d.ts +1 -1
  28. package/dist/identity/agents.d.ts.map +1 -1
  29. package/dist/identity/agents.js +10 -25
  30. package/dist/identity/agents.js.map +1 -1
  31. package/dist/identity/appSession.d.ts +27 -0
  32. package/dist/identity/appSession.d.ts.map +1 -1
  33. package/dist/identity/appSession.js +51 -13
  34. package/dist/identity/appSession.js.map +1 -1
  35. package/dist/identity/seedCredential.d.ts +7 -0
  36. package/dist/identity/seedCredential.d.ts.map +1 -1
  37. package/dist/identity/seedCredential.js +11 -14
  38. package/dist/identity/seedCredential.js.map +1 -1
  39. package/dist/identity/seedStore.d.ts +19 -0
  40. package/dist/identity/seedStore.d.ts.map +1 -1
  41. package/dist/identity/seedStore.js +40 -13
  42. package/dist/identity/seedStore.js.map +1 -1
  43. package/dist/index.d.ts +5 -6
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +4 -5
  46. package/dist/index.js.map +1 -1
  47. package/dist/jsonLd.d.ts +19 -0
  48. package/dist/jsonLd.d.ts.map +1 -0
  49. package/dist/jsonLd.js +24 -0
  50. package/dist/jsonLd.js.map +1 -0
  51. package/dist/mui/AdoptDialog.d.ts.map +1 -1
  52. package/dist/mui/AdoptDialog.js +7 -3
  53. package/dist/mui/AdoptDialog.js.map +1 -1
  54. package/dist/mui/ClearDataDialog.d.ts.map +1 -1
  55. package/dist/mui/ClearDataDialog.js +12 -11
  56. package/dist/mui/ClearDataDialog.js.map +1 -1
  57. package/dist/mui/ConfirmDialog.d.ts +35 -0
  58. package/dist/mui/ConfirmDialog.d.ts.map +1 -0
  59. package/dist/mui/ConfirmDialog.js +18 -0
  60. package/dist/mui/ConfirmDialog.js.map +1 -0
  61. package/dist/mui/LogoutDialog.d.ts.map +1 -1
  62. package/dist/mui/LogoutDialog.js +7 -3
  63. package/dist/mui/LogoutDialog.js.map +1 -1
  64. package/dist/react/documentApp.d.ts +29 -0
  65. package/dist/react/documentApp.d.ts.map +1 -1
  66. package/dist/react/documentApp.js +9 -38
  67. package/dist/react/documentApp.js.map +1 -1
  68. package/dist/react/hooks.d.ts +13 -7
  69. package/dist/react/hooks.d.ts.map +1 -1
  70. package/dist/react/hooks.js +17 -11
  71. package/dist/react/hooks.js.map +1 -1
  72. package/dist/session/authStore.d.ts +9 -9
  73. package/dist/session/authStore.d.ts.map +1 -1
  74. package/dist/session/authStore.js +100 -96
  75. package/dist/session/authStore.js.map +1 -1
  76. package/dist/storage/adopt.d.ts +5 -1
  77. package/dist/storage/adopt.d.ts.map +1 -1
  78. package/dist/storage/adopt.js +8 -24
  79. package/dist/storage/adopt.js.map +1 -1
  80. package/dist/storage/entityStore.d.ts.map +1 -1
  81. package/dist/storage/entityStore.js +27 -20
  82. package/dist/storage/entityStore.js.map +1 -1
  83. package/dist/storage/localStore.d.ts +12 -6
  84. package/dist/storage/localStore.d.ts.map +1 -1
  85. package/dist/storage/localStore.js +118 -74
  86. package/dist/storage/localStore.js.map +1 -1
  87. package/dist/storage/sharedCollectionReader.d.ts +1 -1
  88. package/dist/storage/sharedCollectionReader.d.ts.map +1 -1
  89. package/dist/storage/sharedCollectionReader.js +79 -81
  90. package/dist/storage/sharedCollectionReader.js.map +1 -1
  91. package/dist/storage/storageManager.d.ts +3 -1
  92. package/dist/storage/storageManager.d.ts.map +1 -1
  93. package/dist/storage/storageManager.js +20 -6
  94. package/dist/storage/storageManager.js.map +1 -1
  95. package/dist/storage/syncController.d.ts +1 -16
  96. package/dist/storage/syncController.d.ts.map +1 -1
  97. package/dist/storage/syncController.js +38 -58
  98. package/dist/storage/syncController.js.map +1 -1
  99. package/dist/storage/syncStatusStore.d.ts +8 -6
  100. package/dist/storage/syncStatusStore.d.ts.map +1 -1
  101. package/dist/storage/syncStatusStore.js +6 -6
  102. package/dist/storage/syncStatusStore.js.map +1 -1
  103. package/dist/storage/wasRemoteStore.d.ts +34 -2
  104. package/dist/storage/wasRemoteStore.d.ts.map +1 -1
  105. package/dist/storage/wasRemoteStore.js +45 -7
  106. package/dist/storage/wasRemoteStore.js.map +1 -1
  107. package/dist/storage/wasSync.d.ts.map +1 -1
  108. package/dist/storage/wasSync.js +47 -38
  109. package/dist/storage/wasSync.js.map +1 -1
  110. package/dist/sync/changesQuery.d.ts.map +1 -1
  111. package/dist/sync/changesQuery.js +2 -12
  112. package/dist/sync/changesQuery.js.map +1 -1
  113. package/dist/sync/docCipher.d.ts +17 -9
  114. package/dist/sync/docCipher.d.ts.map +1 -1
  115. package/dist/sync/docCipher.js +17 -11
  116. package/dist/sync/docCipher.js.map +1 -1
  117. package/dist/sync/feedMasterPort.d.ts +6 -0
  118. package/dist/sync/feedMasterPort.d.ts.map +1 -1
  119. package/dist/sync/feedMasterPort.js +82 -40
  120. package/dist/sync/feedMasterPort.js.map +1 -1
  121. package/dist/sync/index.d.ts +6 -5
  122. package/dist/sync/index.d.ts.map +1 -1
  123. package/dist/sync/index.js +6 -3
  124. package/dist/sync/index.js.map +1 -1
  125. package/dist/sync/lww.d.ts +15 -14
  126. package/dist/sync/lww.d.ts.map +1 -1
  127. package/dist/sync/lww.js +3 -3
  128. package/dist/sync/lww.js.map +1 -1
  129. package/dist/sync/lwwConflictHandler.d.ts +3 -10
  130. package/dist/sync/lwwConflictHandler.d.ts.map +1 -1
  131. package/dist/sync/lwwConflictHandler.js +7 -17
  132. package/dist/sync/lwwConflictHandler.js.map +1 -1
  133. package/dist/sync/pushWrites.d.ts +6 -8
  134. package/dist/sync/pushWrites.d.ts.map +1 -1
  135. package/dist/sync/pushWrites.js +56 -83
  136. package/dist/sync/pushWrites.js.map +1 -1
  137. package/dist/sync/types.d.ts +80 -4
  138. package/dist/sync/types.d.ts.map +1 -1
  139. package/dist/sync/types.js +36 -18
  140. package/dist/sync/types.js.map +1 -1
  141. package/dist/sync/wasSyncPort.d.ts +0 -11
  142. package/dist/sync/wasSyncPort.d.ts.map +1 -1
  143. package/dist/sync/wasSyncPort.js +8 -33
  144. package/dist/sync/wasSyncPort.js.map +1 -1
  145. package/package.json +5 -5
  146. package/dist/identity/initAppSession.d.ts +0 -27
  147. package/dist/identity/initAppSession.d.ts.map +0 -1
  148. package/dist/identity/initAppSession.js +0 -30
  149. package/dist/identity/initAppSession.js.map +0 -1
package/README.md CHANGED
@@ -101,9 +101,9 @@ WAS collection `id` (a deliberately unprefixed, generic name shared across
101
101
  interoperable apps). `credential` names the self-issued seed credential the
102
102
  first-run flow mints; its `vocabBase` namespaces the app's own type term only,
103
103
  since every app key also carries the shared `AppKeyCredential` marker type and
104
- its `seed` / `origin` claims under `urn:was:` IRIs. All other fields
105
- (`mediatorBase`, `dbName`, `storageKeyPrefix`, `sync`, `expiry`) are optional
106
- with documented defaults.
104
+ its `seed` / `origin` claims under `https://w3id.org/byoe#` IRIs. All other
105
+ fields (`mediatorBase`, `dbName`, `storageKeyPrefix`, `sync`, `expiry`) are
106
+ optional with documented defaults.
107
107
 
108
108
  A collection may also declare `visibility: 'public'`
109
109
  (`{ key: 'posts', id: 'microblog-posts', visibility: 'public' }`); the default
@@ -124,11 +124,11 @@ carry the `updatedAt` / `clientId` LWW fields like any other collection --
124
124
  without them a concurrent multi-device edit falls back to server-wins.
125
125
 
126
126
  At login, a public collection is requested from the wallet with the distinct
127
- `urn:was:public-collection` descriptor type (private collections use
128
- `urn:was:collection`): the wallet provisions it plaintext with a public-read
129
- policy, shows a world-readable consent warning, and delegates the usual
130
- read/write capability (public covers only unauthenticated reads; writes stay
131
- capability-only). A wallet that predates the descriptor reports the request
127
+ `https://w3id.org/byoe#public-collection` descriptor type (private collections
128
+ use `https://w3id.org/byoe#collection`): the wallet provisions it plaintext with
129
+ a public-read policy, shows a world-readable consent warning, and delegates the
130
+ usual read/write capability (public covers only unauthenticated reads; writes
131
+ stay capability-only). A wallet that predates the descriptor reports the request
132
132
  unsatisfiable rather than silently provisioning a private collection, so the
133
133
  feature fails closed with older wallets. Publicness is granted at consent time
134
134
  by the wallet -- the app itself can never escalate an existing private
@@ -173,14 +173,14 @@ the same `key` or `id` in each is rejected at store open
173
173
  (`validateSharedCollections`).
174
174
 
175
175
  At login each shared collection is requested with the distinct
176
- `urn:was:shared-collection` descriptor type and the read-only action set
177
- `SHARED_ACTIONS` (`GET`/`HEAD`) -- an app never asks to write a wallet
178
- collection. As with `urn:was:public-collection`, a wallet that predates the type
179
- reports the request unsatisfiable and the feature fails closed, which is the
180
- point: a share fuses two axes -- the read zcap AND an entry in the collection's
181
- key-epoch roster -- and a wallet that granted only the zcap would hand the app
182
- ciphertext it cannot decrypt, surfacing as corrupt data rather than as a wallet
183
- that needs updating.
176
+ `https://w3id.org/byoe#shared-collection` descriptor type and the read-only
177
+ action set `SHARED_ACTIONS` (`GET`/`HEAD`) -- an app never asks to write a
178
+ wallet collection. As with `https://w3id.org/byoe#public-collection`, a wallet
179
+ that predates the type reports the request unsatisfiable and the feature fails
180
+ closed, which is the point: a share fuses two axes -- the read zcap AND an entry
181
+ in the collection's key-epoch roster -- and a wallet that granted only the zcap
182
+ would hand the app ciphertext it cannot decrypt, surfacing as corrupt data
183
+ rather than as a wallet that needs updating.
184
184
 
185
185
  Decryption uses the app's **identity** key-agreement key: the X25519 twin of its
186
186
  `did:key` controller (`IdentityAgents.keyAgreementKey`), which is exactly what
@@ -254,8 +254,7 @@ export function Root() {
254
254
  import { useLogin } from '@interop/was-react'
255
255
 
256
256
  export function LoginPage() {
257
- const { login, status, phase, error } = useLogin()
258
- const busy = status === 'authenticating'
257
+ const { login, authenticating: busy, phase, error } = useLogin()
259
258
 
260
259
  return (
261
260
  <div>
@@ -273,12 +272,13 @@ export function LoginPage() {
273
272
 
274
273
  ```tsx
275
274
  import { uuidv7 } from 'uuidv7'
276
- import { getClientId } from '@interop/was-react'
275
+ import { useSession } from '@interop/was-react'
277
276
  import { useNotes } from './stores.js'
278
277
 
279
278
  export function Notes() {
280
279
  const notes = useNotes(state => [...state.byId.values()])
281
280
  const insert = useNotes(state => state.insert)
281
+ const { clientId } = useSession()
282
282
 
283
283
  async function addNote() {
284
284
  const now = new Date().toISOString()
@@ -288,7 +288,7 @@ export function Notes() {
288
288
  body: '',
289
289
  createdAt: now,
290
290
  updatedAt: now,
291
- clientId: getClientId()
291
+ clientId
292
292
  })
293
293
  }
294
294
 
@@ -305,10 +305,12 @@ export function Notes() {
305
305
  }
306
306
  ```
307
307
 
308
- Entity payloads MUST carry `updatedAt` and `clientId` (from `getClientId()`),
309
- stamped on EVERY insert and update: they are the last-write-wins pair that
310
- settles concurrent multi-device edits of the same entity. A payload without them
311
- loses every sync conflict.
308
+ Entity payloads MUST carry `updatedAt` and `clientId`, stamped on EVERY insert
309
+ and update: they are the last-write-wins pair that settles concurrent
310
+ multi-device edits of the same entity. A payload without them loses every sync
311
+ conflict. Read the id from `useSession().clientId` (resolved once per session,
312
+ honoring `storageKeyPrefix`); outside React, call
313
+ `getClientId({ storageKeyPrefix })` with the same prefix your config declares.
312
314
 
313
315
  A public collection can additionally answer server-side equality queries.
314
316
  Declare the queryable content attributes in the collection config:
@@ -393,9 +395,9 @@ a progress line (`connecting` to `verifying`):
393
395
  2. **One popup.** A VPR carrying a `DIDAuthentication` query plus one
394
396
  `AppConnectQuery` -- the app's display name, `credentialType`, and
395
397
  `vocabBase`, a `capabilityQuery` entry per configured collection, and a
396
- `urn:was:shared-collection` entry per shared collection -- is sent via
397
- `navigator.credentials.get` (`buildAppConnectVpr`). A `null` response is a
398
- user cancel (`LoginCancelledError`), not an error.
398
+ `https://w3id.org/byoe#shared-collection` entry per shared collection -- is
399
+ sent via `navigator.credentials.get` (`buildAppConnectVpr`). A `null`
400
+ response is a user cancel (`LoginCancelledError`), not an error.
399
401
  3. **The wallet matches or mints.** On a first run the wallet generates the
400
402
  32-byte seed itself, self-issues the origin-bound app-key credential, and
401
403
  stores it in its own credential store under the same consent; on a returning
@@ -412,8 +414,7 @@ a progress line (`connecting` to `verifying`):
412
414
  and seed-to-DID bound, then recovers the seed.
413
415
  6. **Derive identity.** The stable `did:key` controller and its signer are
414
416
  derived deterministically from the seed via `CapabilityAgent.fromSeed`
415
- (`initAppSession` / `deriveIdentity`). The same seed yields the same identity
416
- on every device.
417
+ (`deriveIdentity`). The same seed yields the same identity on every device.
417
418
  7. **Check grants.** The delegated zcaps ride in the same presentation's
418
419
  top-level `zcap` array. `checkGrants` asserts every zcap is controlled by the
419
420
  app DID, shares one space on a single WAS host, covers each configured
@@ -429,10 +430,11 @@ a progress line (`connecting` to `verifying`):
429
430
  ## Session lifecycle
430
431
 
431
432
  The session is owned by a zustand auth store built once per app by the provider
432
- (`createAuthStore`). Its `status` is `idle` to `restoring` to `unauthenticated`
433
- / `authenticating` / `authenticated`, and it is the router gate: a protected
434
- route waits for the restore attempt to settle before choosing between the app
435
- and the login page.
433
+ (`createAuthStore`). Its `status` is `boot` to `local` / `connected` /
434
+ `reconnect`, and it is the router gate: a protected route waits for the restore
435
+ attempt to settle before choosing between the app and the login page. A login in
436
+ flight is NOT a status -- it is the `phase` field, which `useSession()` /
437
+ `useLogin()` also surface as the derived boolean `authenticating`.
436
438
 
437
439
  - **Restore (zero popups).** On mount, `restore()` reads the persisted session
438
440
  from IndexedDB and, if present and consistent, re-derives the identity, opens
@@ -441,7 +443,8 @@ and the login page.
441
443
  - **Login.** `useLogin().login()` runs the full [login flow](#login-flow).
442
444
  - **Reconnect.** Grants are expiry-only. The store watches the earliest grant
443
445
  expiry and, once within the warning window (`expiry.warningMs`, default 1h) or
444
- after a live 401/403, sets `accessExpired`. `useReconnect().reconnect()`
446
+ after a live 401/403, moves the status to `reconnect` (which `useReconnect()`
447
+ surfaces as the derived `accessExpired`). `useReconnect().reconnect()`
445
448
  re-requests grants with the existing seed (one wallet popup, same identity,
446
449
  same data) and restarts sync.
447
450
  - **Logout.** `useLogout()` stops sync, closes and forgets the local store,
@@ -452,15 +455,15 @@ and the login page.
452
455
 
453
456
  Hooks:
454
457
 
455
- | Hook | Returns |
456
- | ----------------- | --------------------------------------------------------------------------------------- |
457
- | `useSession()` | `status`, `phase`, `error`, `controllerDid`, `expires`, `accessExpired`, `reconnecting` |
458
- | `useLogin()` | `{ login, status, phase, error }` |
459
- | `useLogout()` | `() => Promise<void>` |
460
- | `useReconnect()` | `{ accessExpired, reconnecting, reconnect }` |
461
- | `useSyncStatus()` | `{ state, label, title }` (see below) |
462
- | `useAppReady()` | `{ ready, error }` -- the hydration gate |
463
- | `useAuthStore()` | the bound vanilla store (for `getState().restore()`, etc.) |
458
+ | Hook | Returns |
459
+ | ----------------- | --------------------------------------------------------------------------------------------------------- |
460
+ | `useSession()` | `status`, `phase`, `authenticating`, `error`, `controllerDid`, `expires`, `accessExpired`, `reconnecting` |
461
+ | `useLogin()` | `{ login, authenticating, status, phase, error }` |
462
+ | `useLogout()` | `() => Promise<void>` |
463
+ | `useReconnect()` | `{ accessExpired, reconnecting, reconnect }` |
464
+ | `useSyncStatus()` | `{ state, label, title }` (see below) |
465
+ | `useAppReady()` | `{ ready, error }` -- the hydration gate |
466
+ | `useAuthStore()` | the bound vanilla store (for `getState().restore()`, etc.) |
464
467
 
465
468
  ## Sync architecture
466
469
 
@@ -491,7 +494,10 @@ Hooks:
491
494
  with `sequence`+1 (a mutable-head model); deletes are soft-delete tombstones.
492
495
  - **Status.** `useSyncStatus()` rolls the per-collection replication states up
493
496
  to an aggregate: `offline` (no replication running / local-only), or
494
- `error > syncing > synced`. The `SyncStatusChip` MUI component renders it.
497
+ `error > syncing > synced`. The `SyncStatusChip` MUI component renders it. The
498
+ underlying `useSyncStatusStore().statuses` map is keyed by the registry's
499
+ logical collection `key`, like every other layer (two entries may share one
500
+ WAS collection `id`, so the id is not a unique status key).
495
501
 
496
502
  ## Entry points
497
503
 
@@ -1 +1 @@
1
- {"version":3,"file":"chapi.d.ts","sourceRoot":"","sources":["../../src/auth/chapi.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AAC3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAE1D;;GAEG;AACH,eAAO,MAAM,qBAAqB,sCAAsC,CAAA;AAkExE;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAAC,EAC9B,YAAoC,EACrC,GAAE;IACD,YAAY,CAAC,EAAE,MAAM,CAAA;CACjB,GAAG,OAAO,CAAC,IAAI,CAAC,CAMrB;AAED;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAAC,EAC7B,GAAG,EACH,YAAY,EACb,EAAE;IACD,GAAG,EAAE,WAAW,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,GAAG,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAgB1C"}
1
+ {"version":3,"file":"chapi.d.ts","sourceRoot":"","sources":["../../src/auth/chapi.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AAC3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAE1D;;GAEG;AACH,eAAO,MAAM,qBAAqB,sCAAsC,CAAA;AAgExE;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAAC,EAC9B,YAAoC,EACrC,GAAE;IACD,YAAY,CAAC,EAAE,MAAM,CAAA;CACjB,GAAG,OAAO,CAAC,IAAI,CAAC,CAMrB;AAED;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAAC,EAC7B,GAAG,EACH,YAAY,EACb,EAAE;IACD,GAAG,EAAE,WAAW,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,GAAG,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAgB1C"}
@@ -38,14 +38,15 @@ function e2eBridgeActive() {
38
38
  }
39
39
  let e2eRequestId = 0;
40
40
  /**
41
- * Queues a request for the e2e harness and polls for its response.
41
+ * Queues a `get` request for the e2e harness and polls for its response (the
42
+ * one-popup flow rides `get` alone, so that is the only queued request type).
42
43
  */
43
- async function e2eRoundTrip(type, body) {
44
+ async function e2eRoundTrip(body) {
44
45
  const win = window;
45
46
  win.__WAS_REACT_E2E_CHAPI_REQUESTS__ ??= [];
46
47
  win.__WAS_REACT_E2E_CHAPI_RESPONSES__ ??= {};
47
48
  const id = ++e2eRequestId;
48
- win.__WAS_REACT_E2E_CHAPI_REQUESTS__.push({ id, type, body });
49
+ win.__WAS_REACT_E2E_CHAPI_REQUESTS__.push({ id, type: 'get', body });
49
50
  const deadline = Date.now() + 120_000;
50
51
  while (Date.now() < deadline) {
51
52
  if (id in win.__WAS_REACT_E2E_CHAPI_RESPONSES__) {
@@ -85,7 +86,7 @@ export async function loadChapi({ mediatorBase = DEFAULT_MEDIATOR_BASE } = {}) {
85
86
  export async function chapiGet({ vpr, mediatorBase }) {
86
87
  let wire;
87
88
  if (e2eBridgeActive()) {
88
- wire = await e2eRoundTrip('get', vpr);
89
+ wire = await e2eRoundTrip(vpr);
89
90
  }
90
91
  else {
91
92
  await loadChapi(mediatorBase !== undefined ? { mediatorBase } : {});
@@ -1 +1 @@
1
- {"version":3,"file":"chapi.js","sourceRoot":"","sources":["../../src/auth/chapi.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"chapi.js","sourceRoot":"","sources":["../../src/auth/chapi.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"loginFlow.d.ts","sourceRoot":"","sources":["../../src/auth/loginFlow.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAEV,KAAK,EACN,MAAM,8BAA8B,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAA;AACnE,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,+BAA+B,CAAA;AAEtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAE3D,OAAO,EAGL,KAAK,sBAAsB,EAC5B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhD;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,WAAW,EAAE,sBAAsB,EAAE,CAAA;IACrC;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC5B;;OAEG;IACH,UAAU,EAAE,oBAAoB,CAAA;IAChC;;OAEG;IACH,cAAc,EAAE,cAAc,CAAA;IAC9B;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,WAAW,CAAA;AAEnD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,UAAU,CAAA;IAChB,QAAQ,EAAE,cAAc,CAAA;IACxB,MAAM,EAAE,KAAK,EAAE,CAAA;IACf,MAAM,EAAE,YAAY,CAAA;IACpB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;OAEG;IACH,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,IAAI,EAAE,MAAM;CAIzB;AAED;;;;;GAKG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;;CAQhD;AA6ED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,EAClC,QAAQ,EACR,MAAM,EACN,OAAO,EACR,EAAE;IACD,QAAQ,EAAE,cAAc,CAAA;IACxB,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAA;CACtC,GAAG,OAAO,CAAC,aAAa,CAAC,CAqCzB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CAAC,EACpC,MAAM,EACN,OAAO,EACR,EAAE;IACD,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAA;CACtC,GAAG,OAAO,CAAC,YAAY,CAAC,CAsExB"}
1
+ {"version":3,"file":"loginFlow.d.ts","sourceRoot":"","sources":["../../src/auth/loginFlow.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAEV,KAAK,EACN,MAAM,8BAA8B,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAA;AACnE,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,+BAA+B,CAAA;AAEtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAE3D,OAAO,EAGL,KAAK,sBAAsB,EAC5B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhD;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,WAAW,EAAE,sBAAsB,EAAE,CAAA;IACrC;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC5B;;OAEG;IACH,UAAU,EAAE,oBAAoB,CAAA;IAChC;;OAEG;IACH,cAAc,EAAE,cAAc,CAAA;IAC9B;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,WAAW,CAAA;AAEnD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,UAAU,CAAA;IAChB,QAAQ,EAAE,cAAc,CAAA;IACxB,MAAM,EAAE,KAAK,EAAE,CAAA;IACf,MAAM,EAAE,YAAY,CAAA;IACpB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;OAEG;IACH,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,IAAI,EAAE,MAAM;CAIzB;AAED;;;;;GAKG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;;CAQhD;AA8HD;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,EAClC,QAAQ,EACR,MAAM,EACN,OAAO,EACR,EAAE;IACD,QAAQ,EAAE,cAAc,CAAA;IACxB,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAA;CACtC,GAAG,OAAO,CAAC,aAAa,CAAC,CAczB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CAAC,EACpC,MAAM,EACN,OAAO,EACR,EAAE;IACD,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAA;CACtC,GAAG,OAAO,CAAC,YAAY,CAAC,CAkDxB"}
@@ -1,5 +1,5 @@
1
1
  import { findSeedCredential, parseSeedCredential } from '../identity/seedCredential.js';
2
- import { initAppSession } from '../identity/initAppSession.js';
2
+ import { NO_EXPIRY_MS } from '../identity/appSession.js';
3
3
  import { chapiGet } from './chapi.js';
4
4
  import { buildAppConnectVpr, newChallenge } from './loginRequest.js';
5
5
  import { checkGrants, grantsOf, verifyLoginPresentation } from './verifyResponse.js';
@@ -25,11 +25,6 @@ export class WalletUnsupportedError extends Error {
25
25
  this.name = 'WalletUnsupportedError';
26
26
  }
27
27
  }
28
- /**
29
- * A nominal far-future expiry (ms) used when an app requests no collections (so
30
- * there are no grants and thus no earliest-expiry to report).
31
- */
32
- const NO_GRANTS_EXPIRY_MS = 100 * 365 * 24 * 60 * 60 * 1000;
33
28
  /**
34
29
  * Reads the wallet-provided `presentation.appConnect.firstRun` boolean. Anything
35
30
  * other than boolean `true` (including an absent member -- a returning login) is
@@ -51,10 +46,13 @@ function appConnectFirstRun(presentation) {
51
46
  * not a login failure, so such a login completes with no remote storage and
52
47
  * the missing readers are warned about downstream.
53
48
  *
54
- * Only the app-owned collections are REQUIRED to be covered. A shared
55
- * collection the wallet declined to grant is not a login failure -- the reader
56
- * for it is simply not opened, with a warning -- so its id is not passed to
57
- * `checkGrants`; it still reaches the routing table through `parseGrants`.
49
+ * Only the app-owned collections are REQUIRED to be covered, each at its own
50
+ * class ceiling: the collections are passed to `checkGrants` with their
51
+ * declared visibility, so a `visibility: 'public'` collection requires at most
52
+ * the add-only set a conformant wallet grants. A shared collection the wallet
53
+ * declined to grant is not a login failure -- the reader for it is simply not
54
+ * opened, with a warning -- so it is not passed to `checkGrants`; it still
55
+ * reaches the routing table through `parseGrants`.
58
56
  *
59
57
  * @param options {object}
60
58
  * @param options.presentation {IVerifiablePresentation}
@@ -65,9 +63,8 @@ function appConnectFirstRun(presentation) {
65
63
  * @returns {CheckedGrants}
66
64
  */
67
65
  function checkGrantsForCollections({ presentation, controllerDid, collections, sharedCollections = [] }) {
68
- const collectionIds = collections.map(collection => collection.id);
69
66
  const grants = grantsOf(presentation);
70
- if (collectionIds.length === 0 && grants.length === 0) {
67
+ if (collections.length === 0 && grants.length === 0) {
71
68
  if (sharedCollections.length > 0) {
72
69
  console.warn('The wallet returned no storage grants; the requested shared ' +
73
70
  `collection(s) [${sharedCollections.join(', ')}] were declined, so ` +
@@ -76,28 +73,29 @@ function checkGrantsForCollections({ presentation, controllerDid, collections, s
76
73
  return {
77
74
  grants: [],
78
75
  parsed: { serverUrl: '', spaceId: '', byCollectionId: {} },
79
- expires: new Date(Date.now() + NO_GRANTS_EXPIRY_MS).toISOString()
76
+ expires: new Date(Date.now() + NO_EXPIRY_MS).toISOString()
80
77
  };
81
78
  }
82
79
  return checkGrants({
83
80
  grants,
84
81
  controllerDid,
85
- collections: collectionIds
82
+ collections
86
83
  });
87
84
  }
88
85
  /**
89
- * Re-requests storage grants for `identity` over a fresh App Connect popup and
90
- * validates them. The expired-access reconnect path: the seed already exists, so
91
- * only the grants need renewing. The wallet matches the same app key and
92
- * re-delegates; the returned credential/`appConnect` marker are ignored here.
86
+ * Runs one App Connect round trip: builds the VPR, opens the single CHAPI `get`
87
+ * popup, and verifies the response presentation (cryptographically, plus the
88
+ * challenge/domain binds). Shared by the full login and the reconnect re-grant
89
+ * -- the same request either way; they differ only in what they take from the
90
+ * verified presentation, and in the step a user cancel names.
93
91
  *
94
92
  * @param options {object}
95
- * @param options.identity {IdentityAgents}
96
93
  * @param options.config {LoginConfig}
94
+ * @param options.cancelStep {string} the step a cancel is reported against
97
95
  * @param [options.onPhase] {Function}
98
- * @returns {Promise<CheckedGrants>}
96
+ * @returns {Promise<IVerifiablePresentation>}
99
97
  */
100
- export async function requestGrants({ identity, config, onPhase }) {
98
+ async function runAppConnect({ config, cancelStep, onPhase }) {
101
99
  onPhase?.('connecting');
102
100
  const challenge = newChallenge();
103
101
  const vpr = buildAppConnectVpr({
@@ -117,7 +115,7 @@ export async function requestGrants({ identity, config, onPhase }) {
117
115
  })
118
116
  });
119
117
  if (!presentation) {
120
- throw new LoginCancelledError('storage grants');
118
+ throw new LoginCancelledError(cancelStep);
121
119
  }
122
120
  onPhase?.('verifying');
123
121
  await verifyLoginPresentation({
@@ -126,6 +124,26 @@ export async function requestGrants({ identity, config, onPhase }) {
126
124
  domain: window.location.origin,
127
125
  documentLoader: config.documentLoader
128
126
  });
127
+ return presentation;
128
+ }
129
+ /**
130
+ * Re-requests storage grants for `identity` over a fresh App Connect popup and
131
+ * validates them. The expired-access reconnect path: the seed already exists, so
132
+ * only the grants need renewing. The wallet matches the same app key and
133
+ * re-delegates; the returned credential/`appConnect` marker are ignored here.
134
+ *
135
+ * @param options {object}
136
+ * @param options.identity {IdentityAgents}
137
+ * @param options.config {LoginConfig}
138
+ * @param [options.onPhase] {Function}
139
+ * @returns {Promise<CheckedGrants>}
140
+ */
141
+ export async function requestGrants({ identity, config, onPhase }) {
142
+ const presentation = await runAppConnect({
143
+ config,
144
+ cancelStep: 'storage grants',
145
+ ...(onPhase && { onPhase })
146
+ });
129
147
  return checkGrantsForCollections({
130
148
  presentation,
131
149
  controllerDid: identity.controllerDid,
@@ -150,33 +168,10 @@ export async function requestGrants({ identity, config, onPhase }) {
150
168
  * @returns {Promise<LoginOutcome>}
151
169
  */
152
170
  export async function loginWithWallet({ config, onPhase }) {
153
- onPhase?.('connecting');
154
- const challenge = newChallenge();
155
- const vpr = buildAppConnectVpr({
156
- challenge,
157
- domain: window.location.origin,
158
- appName: config.appName,
159
- credential: config.credential,
160
- collections: config.collections,
161
- ...(config.sharedCollections && {
162
- sharedCollections: config.sharedCollections
163
- })
164
- });
165
- const presentation = await chapiGet({
166
- vpr,
167
- ...(config.mediatorBase !== undefined && {
168
- mediatorBase: config.mediatorBase
169
- })
170
- });
171
- if (!presentation) {
172
- throw new LoginCancelledError('wallet login');
173
- }
174
- onPhase?.('verifying');
175
- await verifyLoginPresentation({
176
- presentation,
177
- challenge,
178
- domain: window.location.origin,
179
- documentLoader: config.documentLoader
171
+ const presentation = await runAppConnect({
172
+ config,
173
+ cancelStep: 'wallet login',
174
+ ...(onPhase && { onPhase })
180
175
  });
181
176
  // The wallet mints the app key on first run, so a response with no app-key
182
177
  // credential is not first run -- it is a wallet that could not satisfy
@@ -198,7 +193,10 @@ export async function loginWithWallet({ config, onPhase }) {
198
193
  });
199
194
  const seed = parsedCredential.seed;
200
195
  const firstRun = appConnectFirstRun(presentation);
201
- const identity = await initAppSession({ seed });
196
+ // The master identity comes off the parse, which already derived it from this
197
+ // seed to check the seed-to-DID binding (the same `deriveIdentity` call, and
198
+ // the same 32-byte seed rule, every other session path applies).
199
+ const identity = parsedCredential.identity;
202
200
  // Grants ride in the SAME response; validate them against the app-key subject
203
201
  // DID the wallet delegated to.
204
202
  const checked = checkGrantsForCollections({
@@ -1 +1 @@
1
- {"version":3,"file":"loginFlow.js","sourceRoot":"","sources":["../../src/auth/loginFlow.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"loginFlow.js","sourceRoot":"","sources":["../../src/auth/loginFlow.ts"],"names":[],"mappings":"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"}
@@ -13,8 +13,16 @@
13
13
  * delegated zcaps embedded in the response VP, so first-run and returning are a
14
14
  * single request/response with no store popup and no separate grants popup.
15
15
  *
16
- * Only collection-scoped capabilities are requested (no whole-space grant). A
17
- * `visibility: 'public'` collection is requested with the distinct descriptor
16
+ * Only collection-scoped capabilities are requested (no whole-space grant),
17
+ * and each request stays within the ACTION CEILING of the descriptor class it
18
+ * uses (the App Connect spec's "Action ceilings" table): a private collection
19
+ * asks for the full vocabulary ({@link RW_ACTIONS}), a public collection at
20
+ * most the add-only set ({@link PUBLIC_ACTIONS}). A configured action set
21
+ * naming an action above a requested collection's ceiling is a configuration
22
+ * error thrown here, at build time -- a conformant wallet can never grant it,
23
+ * so letting it ride would surface later as a failed login instead.
24
+ *
25
+ * A `visibility: 'public'` collection is requested with the distinct descriptor
18
26
  * type `https://w3id.org/byoe#public-collection` (the wallet provisions it plaintext with a
19
27
  * collection-level public-read policy and renders a world-readable consent
20
28
  * warning); wallets that predate the type render it UNSATISFIABLE, which is the
@@ -41,15 +49,37 @@
41
49
  import type { SeedCredentialConfig } from '../identity/seedCredential.js';
42
50
  import type { IVPRDetails } from './walletRequestTypes.js';
43
51
  /**
44
- * Default read/write actions requested on each app collection.
52
+ * Default read/write actions requested on each private app collection -- also
53
+ * the `https://w3id.org/byoe#collection` class ceiling (the full WAS action
54
+ * vocabulary).
45
55
  */
46
56
  export declare const RW_ACTIONS: string[];
57
+ /**
58
+ * The `https://w3id.org/byoe#public-collection` class ceiling: add-only, reads
59
+ * plus `POST`, never `PUT` or `DELETE`. A write to a plaintext world-readable
60
+ * collection is publication under the user's identity and irreversible in
61
+ * practice, so a conformant wallet caps a public grant here no matter what was
62
+ * asked -- and the App Connect spec forbids an application from requiring an
63
+ * action above it.
64
+ */
65
+ export declare const PUBLIC_ACTIONS: string[];
47
66
  /**
48
67
  * The actions requested on a SHARED (wallet-owned) collection: read-only, and
49
68
  * not configurable. A shared collection belongs to the wallet, so an app never
50
69
  * asks to write it.
51
70
  */
52
71
  export declare const SHARED_ACTIONS: string[];
72
+ /**
73
+ * The action ceiling of the descriptor class a collection is requested with
74
+ * (the App Connect spec's "Action ceilings" table): add-only for
75
+ * `visibility: 'public'` (`https://w3id.org/byoe#public-collection`), the full
76
+ * vocabulary otherwise (`https://w3id.org/byoe#collection`). Shares have their
77
+ * own fixed {@link SHARED_ACTIONS} and never consult a configured action set.
78
+ *
79
+ * @param [visibility] {'private' | 'public'}
80
+ * @returns {string[]}
81
+ */
82
+ export declare function actionCeiling(visibility?: 'private' | 'public'): string[];
53
83
  /**
54
84
  * One collection to request a grant for: the WAS collection id plus its
55
85
  * declared visibility (`'private'`, the default, or `'public'`).
@@ -95,8 +125,11 @@ export declare function newChallenge(): string;
95
125
  * @param [options.sharedCollections] {string[]} WAS collection ids of
96
126
  * wallet-owned collections to request read-and-decrypt access to; each gets a
97
127
  * `https://w3id.org/byoe#shared-collection` descriptor with {@link SHARED_ACTIONS}
98
- * @param [options.actions] {string[]} the RW action set (defaults to
99
- * `RW_ACTIONS`)
128
+ * @param [options.actions] {string[]} the action set to request on each app
129
+ * collection; when omitted each collection requests exactly its class
130
+ * ceiling ({@link actionCeiling}). An explicit set naming an action above a
131
+ * requested collection's ceiling throws (a configuration error, surfaced at
132
+ * build time rather than as a failed login)
100
133
  * @returns {IVPRDetails}
101
134
  */
102
135
  export declare function buildAppConnectVpr({ challenge, domain, appName, credential, collections, sharedCollections, actions }: {
@@ -1 +1 @@
1
- {"version":3,"file":"loginRequest.d.ts","sourceRoot":"","sources":["../../src/auth/loginRequest.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAA;AACzE,OAAO,KAAK,EAEV,WAAW,EACZ,MAAM,yBAAyB,CAAA;AAEhC;;GAEG;AACH,eAAO,MAAM,UAAU,UAA2C,CAAA;AAElE;;;;GAIG;AACH,eAAO,MAAM,cAAc,UAAkB,CAAA;AAE7C;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,EAAE,EAAE,MAAM,CAAA;IACV;;;;OAIG;IACH,UAAU,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;CAClC;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,SAAS,EACT,MAAM,EACN,OAAO,EACP,UAAU,EACV,WAAW,EACX,iBAAsB,EACtB,OAAoB,EACrB,EAAE;IACD,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,oBAAoB,CAAA;IAChC,WAAW,EAAE,sBAAsB,EAAE,CAAA;IACrC,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC5B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB,GAAG,WAAW,CA2Cd"}
1
+ {"version":3,"file":"loginRequest.d.ts","sourceRoot":"","sources":["../../src/auth/loginRequest.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAA;AACzE,OAAO,KAAK,EAEV,WAAW,EACZ,MAAM,yBAAyB,CAAA;AAEhC;;;;GAIG;AACH,eAAO,MAAM,UAAU,UAA2C,CAAA;AAElE;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,UAA0B,CAAA;AAErD;;;;GAIG;AACH,eAAO,MAAM,cAAc,UAAkB,CAAA;AAE7C;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,UAAU,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,MAAM,EAAE,CAEzE;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,EAAE,EAAE,MAAM,CAAA;IACV;;;;OAIG;IACH,UAAU,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;CAClC;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AA+CD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,SAAS,EACT,MAAM,EACN,OAAO,EACP,UAAU,EACV,WAAW,EACX,iBAAsB,EACtB,OAAO,EACR,EAAE;IACD,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,oBAAoB,CAAA;IAChC,WAAW,EAAE,sBAAsB,EAAE,CAAA;IACrC,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC5B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB,GAAG,WAAW,CA2Cd"}
@@ -1,19 +1,77 @@
1
1
  /**
2
- * Default read/write actions requested on each app collection.
2
+ * Default read/write actions requested on each private app collection -- also
3
+ * the `https://w3id.org/byoe#collection` class ceiling (the full WAS action
4
+ * vocabulary).
3
5
  */
4
6
  export const RW_ACTIONS = ['GET', 'HEAD', 'PUT', 'POST', 'DELETE'];
7
+ /**
8
+ * The `https://w3id.org/byoe#public-collection` class ceiling: add-only, reads
9
+ * plus `POST`, never `PUT` or `DELETE`. A write to a plaintext world-readable
10
+ * collection is publication under the user's identity and irreversible in
11
+ * practice, so a conformant wallet caps a public grant here no matter what was
12
+ * asked -- and the App Connect spec forbids an application from requiring an
13
+ * action above it.
14
+ */
15
+ export const PUBLIC_ACTIONS = ['GET', 'HEAD', 'POST'];
5
16
  /**
6
17
  * The actions requested on a SHARED (wallet-owned) collection: read-only, and
7
18
  * not configurable. A shared collection belongs to the wallet, so an app never
8
19
  * asks to write it.
9
20
  */
10
21
  export const SHARED_ACTIONS = ['GET', 'HEAD'];
22
+ /**
23
+ * The action ceiling of the descriptor class a collection is requested with
24
+ * (the App Connect spec's "Action ceilings" table): add-only for
25
+ * `visibility: 'public'` (`https://w3id.org/byoe#public-collection`), the full
26
+ * vocabulary otherwise (`https://w3id.org/byoe#collection`). Shares have their
27
+ * own fixed {@link SHARED_ACTIONS} and never consult a configured action set.
28
+ *
29
+ * @param [visibility] {'private' | 'public'}
30
+ * @returns {string[]}
31
+ */
32
+ export function actionCeiling(visibility) {
33
+ return visibility === 'public' ? PUBLIC_ACTIONS : RW_ACTIONS;
34
+ }
11
35
  /**
12
36
  * A fresh nonce for a VPR challenge.
13
37
  */
14
38
  export function newChallenge() {
15
39
  return crypto.randomUUID();
16
40
  }
41
+ /**
42
+ * The actions to request for one collection: the configured set capped at the
43
+ * collection's class ceiling, kept in ceiling order. When no set is configured
44
+ * the whole ceiling is requested. An EXPLICIT set naming an action above the
45
+ * ceiling is a configuration error thrown here, at request build time: a
46
+ * conformant wallet can never grant it, so silently capping would leave the
47
+ * app believing it asked for more than any correct wallet returns, and the
48
+ * mismatch would surface later as a failed login instead of as the config bug
49
+ * it is.
50
+ *
51
+ * @param options {object}
52
+ * @param options.id {string} the WAS collection id (for error messages)
53
+ * @param [options.visibility] {'private' | 'public'}
54
+ * @param [options.actions] {string[]} the configured action set
55
+ * @returns {string[]}
56
+ */
57
+ function requestedActions({ id, visibility, actions }) {
58
+ const ceiling = actionCeiling(visibility);
59
+ if (actions === undefined) {
60
+ return ceiling;
61
+ }
62
+ const excess = actions.filter(action => !ceiling.includes(action));
63
+ if (excess.length > 0) {
64
+ throw new Error(`Collection "${id}" cannot request action(s) above its class ceiling: ` +
65
+ `${excess.join(', ')} (the ceiling is ${ceiling.join(', ')}).`);
66
+ }
67
+ const capped = ceiling.filter(action => actions.includes(action));
68
+ if (capped.length === 0) {
69
+ // An empty allowedAction array means EVERY action in the zcap model, so an
70
+ // action-less request must never be built.
71
+ throw new Error(`Collection "${id}" requests no actions.`);
72
+ }
73
+ return capped;
74
+ }
17
75
  /**
18
76
  * The one-popup App Connect VPR: DIDAuthentication + a single `AppConnectQuery`.
19
77
  *
@@ -39,14 +97,17 @@ export function newChallenge() {
39
97
  * @param [options.sharedCollections] {string[]} WAS collection ids of
40
98
  * wallet-owned collections to request read-and-decrypt access to; each gets a
41
99
  * `https://w3id.org/byoe#shared-collection` descriptor with {@link SHARED_ACTIONS}
42
- * @param [options.actions] {string[]} the RW action set (defaults to
43
- * `RW_ACTIONS`)
100
+ * @param [options.actions] {string[]} the action set to request on each app
101
+ * collection; when omitted each collection requests exactly its class
102
+ * ceiling ({@link actionCeiling}). An explicit set naming an action above a
103
+ * requested collection's ceiling throws (a configuration error, surfaced at
104
+ * build time rather than as a failed login)
44
105
  * @returns {IVPRDetails}
45
106
  */
46
- export function buildAppConnectVpr({ challenge, domain, appName, credential, collections, sharedCollections = [], actions = RW_ACTIONS }) {
107
+ export function buildAppConnectVpr({ challenge, domain, appName, credential, collections, sharedCollections = [], actions }) {
47
108
  const capabilityQuery = collections.map(({ id, visibility }) => ({
48
109
  referenceId: id,
49
- allowedAction: actions,
110
+ allowedAction: requestedActions({ id, visibility, actions }),
50
111
  invocationTarget: {
51
112
  type: visibility === 'public'
52
113
  ? 'https://w3id.org/byoe#public-collection'
@@ -1 +1 @@
1
- {"version":3,"file":"loginRequest.js","sourceRoot":"","sources":["../../src/auth/loginRequest.ts"],"names":[],"mappings":"AA8CA;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAA;AAElE;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;AAmB7C;;GAEG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO,MAAM,CAAC,UAAU,EAAE,CAAA;AAC5B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,kBAAkB,CAAC,EACjC,SAAS,EACT,MAAM,EACN,OAAO,EACP,UAAU,EACV,WAAW,EACX,iBAAiB,GAAG,EAAE,EACtB,OAAO,GAAG,UAAU,EASrB;IACC,MAAM,eAAe,GAAiC,WAAW,CAAC,GAAG,CACnE,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC;QACvB,WAAW,EAAE,EAAE;QACf,aAAa,EAAE,OAAO;QACtB,gBAAgB,EAAE;YAChB,IAAI,EACF,UAAU,KAAK,QAAQ;gBACrB,CAAC,CAAC,yCAAyC;gBAC3C,CAAC,CAAC,kCAAkC;YACxC,IAAI,EAAE,EAAE;SACT;KACF,CAAC,CACH,CAAA;IACD,KAAK,MAAM,EAAE,IAAI,iBAAiB,EAAE,CAAC;QACnC,eAAe,CAAC,IAAI,CAAC;YACnB,WAAW,EAAE,EAAE;YACf,aAAa,EAAE,cAAc;YAC7B,gBAAgB,EAAE;gBAChB,IAAI,EAAE,yCAAyC;gBAC/C,IAAI,EAAE,EAAE;aACT;SACF,CAAC,CAAA;IACJ,CAAC;IACD,OAAO;QACL,KAAK,EAAE;YACL;gBACE,IAAI,EAAE,mBAAmB;gBACzB,eAAe,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;aACrC;YACD;gBACE,IAAI,EAAE,iBAAiB;gBACvB,GAAG,EAAE;oBACH,IAAI,EAAE,OAAO;oBACb,cAAc,EAAE,UAAU,CAAC,cAAc;oBACzC,SAAS,EAAE,UAAU,CAAC,SAAS;iBAChC;gBACD,eAAe;aAChB;SACF;QACD,SAAS;QACT,MAAM;KACP,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"loginRequest.js","sourceRoot":"","sources":["../../src/auth/loginRequest.ts"],"names":[],"mappings":"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"}