Skip to content

feat(auth): PP-aware SEP-10 verify + per-PP entity approval#110

Merged
AquiGorka merged 2 commits into
mainfrom
feat/sep10-pp-aware-with-kyc-url
Jun 4, 2026
Merged

feat(auth): PP-aware SEP-10 verify + per-PP entity approval#110
AquiGorka merged 2 commits into
mainfrom
feat/sep10-pp-aware-with-kyc-url

Conversation

@AquiGorka

@AquiGorka AquiGorka commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Summary

SEP-10 verify becomes PP-scoped. The wallet posts ppPublicKey alongside the signed challenge; the response carries per-PP entityStatus + the operator-configured KYC submission URL for that PP. A wallet APPROVED on PP-A no longer appears APPROVED on PP-B.

Schema changes (migration 0017_pp_aware_entity_status_and_kyc_url)

  • payment_providers.kyc_submission_url (text, nullable) — per-PP KYC URL surfaced to the wallet when the submitter is not yet APPROVED.
  • pp_entity_approvals (new table): (pp_public_key, account_pubkey)EntityStatus + timestamps, unique on the pair. Sole source of truth for per-PP approval state.
  • entities.statusdropped. Eliminates the dual-write that the old global column would have required.

Pipeline changes

  • requestSchema for POST /v1/stellar/auth requires ppPublicKey: z.string().min(1).
  • responseSchema carries { jwt, entityStatus, kycSubmissionUrl }.
  • P_UpdateChallengeDB returns the narrow ContextWithJWTAndPpPublicKey (just ppPublicKey, not the full request body — principle of least information).
  • New P_AttachEntityStatus looks up the per-PP approval row + pp.kyc_submission_url and fills the response shape.
  • POST /v1/entities writes/promotes a per-PP approval row (UNVERIFIED → APPROVED) instead of mutating entity.status.
  • add-bundle.process gates on the per-PP approval row.

Test

Full suite green locally (deno task test: 200 passed).

Cross-linked PRs

Test plan

  • CI green
  • Migration applies cleanly (drops entities.status, adds kyc_submission_url + pp_entity_approvals)
  • SEP-10 verify call with ppPublicKey returns {jwt, entityStatus, kycSubmissionUrl}
  • POST /v1/entities creates a per-PP approval row, not an entity.status write
  • Bundle submit rejects when no per-PP approval row exists for the submitter

AquiGorka added 2 commits June 4, 2026 13:09
SEP-10 verify is now PP-scoped: the wallet posts `ppPublicKey` alongside
the signed challenge, and the response carries per-PP `entityStatus` +
the operator-configured KYC submission URL for that PP. A wallet
APPROVED on PP-A no longer appears APPROVED on PP-B.

- Verify endpoint schema requires `ppPublicKey: z.string().min(1)`
- New pp_entity_approvals table: (pp_public_key, account_pubkey) →
  EntityStatus + timestamps, with a unique constraint on the pair
- Dropped the global `entities.status` column — sole source of truth
  now lives in pp_entity_approvals
- `payment_providers.kyc_submission_url` carries the per-PP KYC URL
- POST /v1/entities writes/promotes a per-PP approval row instead of
  mutating entity.status
- Bundle add-process gates on the per-PP approval row, not on the old
  global entity status
- `P_UpdateChallengeDB` return narrowed to `ContextWithJWTAndPpPublicKey`
  (passes `ppPublicKey` only — principle of least information)

Migration: 0017_pp_aware_entity_status_and_kyc_url.sql
AquiGorka added a commit to Moonlight-Protocol/local-dev that referenced this pull request Jun 4, 2026
…p-dist default (#111)

## Summary

Keeps Spec 03 (recording rig's private-transfer demo) green against the
corrected wallet + PP-aware provider-platform.

### Spec 03 rewrite — two-pass per user

Each user (Bob, then Alice) runs setup TWICE in their own context. Pass
1 runs the full setup (browser-wallet onboard → Freighter onboard +
local network → channel → add provider + connect) then
`followKycLinkOut`: clicks the "Submit KYC" anchor that the corrected
wallet renders, drives provider-console's
`#/entities/register?provider=<pp>` route end-to-end (Connect Wallet →
Stellar Wallets Kit modal → Freighter → signMessage approve → submit),
then tears the context down. Pass 2 launches a fresh context, redoes
setup (same mnemonic = same account = `entityStatus=APPROVED` for the PP
from pass 1, so the KYC link isn't surfaced), and runs the remaining
beats (receive / deposit / send / balance / withdraw / public toggles).

`addAndConnectProvider` is split into `addProviderAndConnect` (no KYC
follow) + `followKycLinkOut` so pass 1 can drive the KYC link-out
explicitly while pass 2 just connects and moves on.

### Freighter warm-up fixture

`recording/playwright/fixtures/freighter-setup.ts` (new). After
`setupFreighterAccount` imports the user's secret it calls
`addLocalNetwork(page)` (or `switchToTestnet`) so Freighter is in the
same warmed-up state that the verification rig's `setupUserContext`
leaves it in before its own `withWalletApproval` call sites fire.
Without that network step the signMessage popup opens onto Freighter's
Welcome screen and the approve UI never renders.

### `BROWSER_WALLET_PATH` default

`recording/playwright/fixtures/wallet-context.ts` now defaults
`BROWSER_WALLET_PATH` to a `__dirname`-relative path mirroring
`playwright.config.ts`. Previously the default was
`~/repos/browser-wallet/dist`, pinning every run to the user's primary
repo regardless of which clone the rig was launched from.

### Other touchpoints

- `playwright/fixtures/freighter.ts`: `SEL_APPROVE_BUTTON` now matches
Freighter v5.39+ data-testids (`grant-access-connect-button`,
`sign-message-approve-button`, etc.) before the text fallbacks.
- `setup-pp.ts`: registers each seeded PP with a `kycSubmissionUrl`
pointing at `provider-console/#/entities/register?provider=<pp>` so the
recording stack mirrors what an operator configures.
- `recording/setup-recording-keys.ts` +
`recording/playwright/helpers/run-env.ts`: expose Alice/Bob secret keys
so `setupFreighterForKyc` can import them.

### Smoke run

`RUN_ID=phase4-smoke npx playwright test --project=chromium --grep "03
—"` → **1 passed (11.0m)**, driving every beat including both Freighter
signMessage popups.

## Cross-linked PRs

- provider-platform:
Moonlight-Protocol/provider-platform#110
- browser-wallet:
Moonlight-Protocol/browser-wallet#21

## Notes

- 5 pre-existing lint errors in `setup-pp.ts`,
`playwright/fixtures/freighter.ts`, `recording/recording-keys.ts` (npm:
import prefixes, sloppy import, unused `page` param on a pre-existing
fixture). All predate this branch. The repo's `fmt-lint.yml` CI doesn't
lint `recording/` or `playwright/` paths so they don't gate the merge;
not touched here to keep the change scoped.
- `deno.json` has no version field in this repo, so no deno.json
patch-bump applies. (provider-platform's auto-tag is the version-driven
release; this repo has no equivalent.)

## Test plan

- [ ] CI green (fmt-lint matrix on testnet / lifecycle / e2e dirs —
recording/playwright untouched)
- [ ] Spec 03 passes locally end-to-end against the cross-linked
browser-wallet build
- [ ] Pass 2's wallet shows the provider as Connected without a "Submit
KYC" anchor (entityStatus APPROVED from pass 1)
AquiGorka added a commit to Moonlight-Protocol/browser-wallet that referenced this pull request Jun 4, 2026
…KYC (#21)

## Summary

Corrective on #20 — three coupled rewrites bringing the Add Provider
flow + storage shape + KYC handling back on-spec.

### 1. Single URL input

The Add Provider form takes ONE field — the URL — and validates that its
last path segment is a 56-char Stellar G-address. The `#provider-pubkey`
input is gone. Pubkey is extracted from the URL at call-time via a new
`extractPpPubkeyFromUrl(url)` helper in `services/pp-url.ts`. Persisted
shape reverts to `{ id, name, url }` (plus per-account `sessions`).

### 2. PP-aware verify body + per-PP session

`PrivacyProviderClient.postAuth(signedChallenge, ppPublicKey)` posts
both fields so SEP-10 verify returns per-PP `entityStatus` +
`kycSubmissionUrl`. `PrivateChannelsState` bumped to v4; per-account
`sessions[accountId]` carries `{ token, expiresAt, entityStatus,
kycSubmissionUrl }`.

### 3. KYC link-out

`submit-entity-kyc` background handler + popup API gone. When
`session.entityStatus !== "APPROVED"` and `session.kycSubmissionUrl !==
null`, the channel-picker sheet renders an `<a target="_blank">`
pointing at the provider-console KYC route the operator configured
(server-side `payment_providers.kyc_submission_url`).

### Stale-URL detection

Persisted PPs whose URL has no pubkey path segment surface a per-row
"Provider \<label\> needs re-adding — URL format updated." with a
one-click Remove. No auto-delete. No backfill (no discovery endpoint
exists).

## Cross-linked PRs

- provider-platform:
Moonlight-Protocol/provider-platform#110
- local-dev: Moonlight-Protocol/local-dev#111

## Test plan

- [ ] CI green (fmt + lint)
- [ ] Add Provider with a URL ending in `/G…` (56-char G-address)
succeeds
- [ ] Add Provider with a URL lacking the G-address path segment is
rejected with the inline error
- [ ] On connect, the sheet shows "Connected — not authorized yet." with
a Submit KYC link that opens
`provider-console/#/entities/register?provider=<pp>` in a new tab
- [ ] Pre-existing stored PPs with no pubkey path segment surface the
stale-URL notice + Remove
- [ ] Spec 03 passes against this build (covered in local-dev
cross-link)
@AquiGorka AquiGorka merged commit 4f6e29b into main Jun 4, 2026
11 of 13 checks passed
@AquiGorka AquiGorka deleted the feat/sep10-pp-aware-with-kyc-url branch June 4, 2026 17:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant