You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(rp-login): add walletProfile — ask who this site knows you as
#144 made `proxyLogin({})` resolve-or-bind, and that is enough for an RP
that only needs a token. It is not enough for the two RPs we actually
have. `vtc-service/admin-ui` and `did-hosting-ui` both bind their
`/auth/challenge` to the persona DID:
POST /auth/challenge { did: entry.principalDid }
proxyLogin({ entryId, nonce: challenge })
The DID is needed *before* the nonce, so it cannot come out of
`proxyLogin` — and both pages get it today from `vaultList()`, which
enumerates the user's whole vault to answer a question about one entry.
On a site with nothing bound that returns empty and the page gives up,
which is exactly what #144 was meant to stop and could not reach.
`walletProfile({ target })` answers that question and only that one. It
resolves the entry for the browser-attested origin, or raises the
first-use picker and binds the answer, and returns `{ did, entryId,
bound }`. It mints nothing and issues no session.
**`entryId` comes back deliberately.** The objection to a page holding
one was never possession; it was that *learning* one cost a
vault-enumerating prompt. An id handed back for the site's own entry
costs nothing, and passing it to `proxyLogin` saves the lookup this call
just did — so the two-call flow is the same number of VTA round trips as
the `vaultList` + `proxyLogin` it replaces, with one fewer prompt and no
disclosure of the rest of the vault.
**Two prompts on a first sign-in, one after.** Binding raises the
picker; the sign-in raises its own consent. Folding the second into the
first would mean a call that mints nothing silently pre-authorizing one
that does. First contact with a site is the place to ask twice.
An already-bound lookup does not prompt at all: it discloses one DID, to
the site that DID exists for, which is about to receive it inside an
id_token anyway. `principalDid` is read back from the VTA rather than
remembered, since it is maintainer-derived and an entry whose secret was
rotated there would otherwise report a DID it no longer signs as.
Two drift traps found on the way, both now guarded:
- `provider.ts` inlined a second copy of the `BridgeMethod` union, so
adding a method failed at its own call site with an error naming
every method but the new one. It uses the type now.
- `content.ts` cannot `import` (classic script), so it inlines the
protocol constants under a "keep these in sync" comment with nothing
enforcing it. Two invariants ride on that hand-sync: a method routed
to a type absent from `PAGE_FACING_RUNTIME_TYPES` takes its origin
from the page's own message body rather than the browser — and every
vault entry, trust record and pin here is keyed on origin — while a
mistyped constant routes to no handler and surfaces as a shapeless
failure. `tests/page-facing-surface.test.mts` reads both sources and
checks them against each other; its first assertions guard against
the regexes silently matching nothing.
Requires the RP-side change in OpenVTC/verifiable-trust-infrastructure
and OpenVTC/affinidi-webvh-service; neither wallet call is removed, so
the repos can land in either order.
Signed-off-by: Glenn Gore <glenn.g@affinidi.com>
0 commit comments