@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.
- package/README.md +50 -44
- package/dist/auth/chapi.d.ts.map +1 -1
- package/dist/auth/chapi.js +5 -4
- package/dist/auth/chapi.js.map +1 -1
- package/dist/auth/loginFlow.d.ts.map +1 -1
- package/dist/auth/loginFlow.js +48 -50
- package/dist/auth/loginFlow.js.map +1 -1
- package/dist/auth/loginRequest.d.ts +38 -5
- package/dist/auth/loginRequest.d.ts.map +1 -1
- package/dist/auth/loginRequest.js +66 -5
- package/dist/auth/loginRequest.js.map +1 -1
- package/dist/auth/verifyResponse.d.ts +7 -4
- package/dist/auth/verifyResponse.d.ts.map +1 -1
- package/dist/auth/verifyResponse.js +23 -13
- package/dist/auth/verifyResponse.js.map +1 -1
- package/dist/auth/walletRequestTypes.d.ts +9 -90
- package/dist/auth/walletRequestTypes.d.ts.map +1 -1
- package/dist/config.d.ts +9 -9
- package/dist/config.js +0 -0
- package/dist/dev/provisionDevGrants.d.ts.map +1 -1
- package/dist/dev/provisionDevGrants.js +18 -19
- package/dist/dev/provisionDevGrants.js.map +1 -1
- package/dist/grants.d.ts +19 -9
- package/dist/grants.d.ts.map +1 -1
- package/dist/grants.js +17 -2
- package/dist/grants.js.map +1 -1
- package/dist/identity/agents.d.ts +1 -1
- package/dist/identity/agents.d.ts.map +1 -1
- package/dist/identity/agents.js +10 -25
- package/dist/identity/agents.js.map +1 -1
- package/dist/identity/appSession.d.ts +27 -0
- package/dist/identity/appSession.d.ts.map +1 -1
- package/dist/identity/appSession.js +51 -13
- package/dist/identity/appSession.js.map +1 -1
- package/dist/identity/seedCredential.d.ts +7 -0
- package/dist/identity/seedCredential.d.ts.map +1 -1
- package/dist/identity/seedCredential.js +11 -14
- package/dist/identity/seedCredential.js.map +1 -1
- package/dist/identity/seedStore.d.ts +19 -0
- package/dist/identity/seedStore.d.ts.map +1 -1
- package/dist/identity/seedStore.js +40 -13
- package/dist/identity/seedStore.js.map +1 -1
- package/dist/index.d.ts +5 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -5
- package/dist/index.js.map +1 -1
- package/dist/jsonLd.d.ts +19 -0
- package/dist/jsonLd.d.ts.map +1 -0
- package/dist/jsonLd.js +24 -0
- package/dist/jsonLd.js.map +1 -0
- package/dist/mui/AdoptDialog.d.ts.map +1 -1
- package/dist/mui/AdoptDialog.js +7 -3
- package/dist/mui/AdoptDialog.js.map +1 -1
- package/dist/mui/ClearDataDialog.d.ts.map +1 -1
- package/dist/mui/ClearDataDialog.js +12 -11
- package/dist/mui/ClearDataDialog.js.map +1 -1
- package/dist/mui/ConfirmDialog.d.ts +35 -0
- package/dist/mui/ConfirmDialog.d.ts.map +1 -0
- package/dist/mui/ConfirmDialog.js +18 -0
- package/dist/mui/ConfirmDialog.js.map +1 -0
- package/dist/mui/LogoutDialog.d.ts.map +1 -1
- package/dist/mui/LogoutDialog.js +7 -3
- package/dist/mui/LogoutDialog.js.map +1 -1
- package/dist/react/documentApp.d.ts +29 -0
- package/dist/react/documentApp.d.ts.map +1 -1
- package/dist/react/documentApp.js +9 -38
- package/dist/react/documentApp.js.map +1 -1
- package/dist/react/hooks.d.ts +13 -7
- package/dist/react/hooks.d.ts.map +1 -1
- package/dist/react/hooks.js +17 -11
- package/dist/react/hooks.js.map +1 -1
- package/dist/session/authStore.d.ts +9 -9
- package/dist/session/authStore.d.ts.map +1 -1
- package/dist/session/authStore.js +100 -96
- package/dist/session/authStore.js.map +1 -1
- package/dist/storage/adopt.d.ts +5 -1
- package/dist/storage/adopt.d.ts.map +1 -1
- package/dist/storage/adopt.js +8 -24
- package/dist/storage/adopt.js.map +1 -1
- package/dist/storage/entityStore.d.ts.map +1 -1
- package/dist/storage/entityStore.js +27 -20
- package/dist/storage/entityStore.js.map +1 -1
- package/dist/storage/localStore.d.ts +12 -6
- package/dist/storage/localStore.d.ts.map +1 -1
- package/dist/storage/localStore.js +118 -74
- package/dist/storage/localStore.js.map +1 -1
- package/dist/storage/sharedCollectionReader.d.ts +1 -1
- package/dist/storage/sharedCollectionReader.d.ts.map +1 -1
- package/dist/storage/sharedCollectionReader.js +79 -81
- package/dist/storage/sharedCollectionReader.js.map +1 -1
- package/dist/storage/storageManager.d.ts +3 -1
- package/dist/storage/storageManager.d.ts.map +1 -1
- package/dist/storage/storageManager.js +20 -6
- package/dist/storage/storageManager.js.map +1 -1
- package/dist/storage/syncController.d.ts +1 -16
- package/dist/storage/syncController.d.ts.map +1 -1
- package/dist/storage/syncController.js +38 -58
- package/dist/storage/syncController.js.map +1 -1
- package/dist/storage/syncStatusStore.d.ts +8 -6
- package/dist/storage/syncStatusStore.d.ts.map +1 -1
- package/dist/storage/syncStatusStore.js +6 -6
- package/dist/storage/syncStatusStore.js.map +1 -1
- package/dist/storage/wasRemoteStore.d.ts +34 -2
- package/dist/storage/wasRemoteStore.d.ts.map +1 -1
- package/dist/storage/wasRemoteStore.js +45 -7
- package/dist/storage/wasRemoteStore.js.map +1 -1
- package/dist/storage/wasSync.d.ts.map +1 -1
- package/dist/storage/wasSync.js +47 -38
- package/dist/storage/wasSync.js.map +1 -1
- package/dist/sync/changesQuery.d.ts.map +1 -1
- package/dist/sync/changesQuery.js +2 -12
- package/dist/sync/changesQuery.js.map +1 -1
- package/dist/sync/docCipher.d.ts +17 -9
- package/dist/sync/docCipher.d.ts.map +1 -1
- package/dist/sync/docCipher.js +17 -11
- package/dist/sync/docCipher.js.map +1 -1
- package/dist/sync/feedMasterPort.d.ts +6 -0
- package/dist/sync/feedMasterPort.d.ts.map +1 -1
- package/dist/sync/feedMasterPort.js +82 -40
- package/dist/sync/feedMasterPort.js.map +1 -1
- package/dist/sync/index.d.ts +6 -5
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +6 -3
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/lww.d.ts +15 -14
- package/dist/sync/lww.d.ts.map +1 -1
- package/dist/sync/lww.js +3 -3
- package/dist/sync/lww.js.map +1 -1
- package/dist/sync/lwwConflictHandler.d.ts +3 -10
- package/dist/sync/lwwConflictHandler.d.ts.map +1 -1
- package/dist/sync/lwwConflictHandler.js +7 -17
- package/dist/sync/lwwConflictHandler.js.map +1 -1
- package/dist/sync/pushWrites.d.ts +6 -8
- package/dist/sync/pushWrites.d.ts.map +1 -1
- package/dist/sync/pushWrites.js +56 -83
- package/dist/sync/pushWrites.js.map +1 -1
- package/dist/sync/types.d.ts +80 -4
- package/dist/sync/types.d.ts.map +1 -1
- package/dist/sync/types.js +36 -18
- package/dist/sync/types.js.map +1 -1
- package/dist/sync/wasSyncPort.d.ts +0 -11
- package/dist/sync/wasSyncPort.d.ts.map +1 -1
- package/dist/sync/wasSyncPort.js +8 -33
- package/dist/sync/wasSyncPort.js.map +1 -1
- package/package.json +5 -5
- package/dist/identity/initAppSession.d.ts +0 -27
- package/dist/identity/initAppSession.d.ts.map +0 -1
- package/dist/identity/initAppSession.js +0 -30
- 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 `
|
|
105
|
-
(`mediatorBase`, `dbName`, `storageKeyPrefix`, `sync`, `expiry`) are
|
|
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
|
-
`
|
|
128
|
-
`
|
|
129
|
-
policy, shows a world-readable consent warning, and delegates the
|
|
130
|
-
read/write capability (public covers only unauthenticated reads; writes
|
|
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
|
-
`
|
|
177
|
-
`SHARED_ACTIONS` (`GET`/`HEAD`) -- an app never asks to write a
|
|
178
|
-
collection. As with `
|
|
179
|
-
reports the request unsatisfiable and the feature fails
|
|
180
|
-
point: a share fuses two axes -- the read zcap AND an entry
|
|
181
|
-
key-epoch roster -- and a wallet that granted only the zcap
|
|
182
|
-
ciphertext it cannot decrypt, surfacing as corrupt data
|
|
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,
|
|
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 {
|
|
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
|
|
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
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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
|
-
`
|
|
397
|
-
`navigator.credentials.get` (`buildAppConnectVpr`). A `null`
|
|
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
|
-
(`
|
|
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 `
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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,
|
|
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
|
|
package/dist/auth/chapi.d.ts.map
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/auth/chapi.js
CHANGED
|
@@ -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(
|
|
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(
|
|
89
|
+
wire = await e2eRoundTrip(vpr);
|
|
89
90
|
}
|
|
90
91
|
else {
|
|
91
92
|
await loadChapi(mediatorBase !== undefined ? { mediatorBase } : {});
|
package/dist/auth/chapi.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chapi.js","sourceRoot":"","sources":["../../src/auth/chapi.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qCAAqC;AACrC;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAA;AAItD;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,mCAAmC,CAAA;AAoBxE;;;;;;GAMG;AACH,SAAS,SAAS;IAChB,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,YAAY,CAAA;AAC9C,CAAC;AAED,SAAS,eAAe;IACtB,OAAO,CACL,SAAS,EAAE,KAAK,YAAY;QAC3B,MAA0B,CAAC,uBAAuB,KAAK,IAAI,CAC7D,CAAA;AACH,CAAC;AAED,IAAI,YAAY,GAAG,CAAC,CAAA;AAEpB
|
|
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;
|
|
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"}
|
package/dist/auth/loginFlow.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { findSeedCredential, parseSeedCredential } from '../identity/seedCredential.js';
|
|
2
|
-
import {
|
|
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
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
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 (
|
|
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() +
|
|
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
|
|
82
|
+
collections
|
|
86
83
|
});
|
|
87
84
|
}
|
|
88
85
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
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<
|
|
96
|
+
* @returns {Promise<IVerifiablePresentation>}
|
|
99
97
|
*/
|
|
100
|
-
|
|
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(
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
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":"AA6BA,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EAEpB,MAAM,+BAA+B,CAAA;AACtC,OAAO,EAAE,
|
|
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)
|
|
17
|
-
*
|
|
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
|
|
99
|
-
*
|
|
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
|
|
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
|
|
43
|
-
*
|
|
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
|
|
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":"
|
|
1
|
+
{"version":3,"file":"loginRequest.js","sourceRoot":"","sources":["../../src/auth/loginRequest.ts"],"names":[],"mappings":"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"}
|