feat(auth): PP-aware SEP-10 verify + per-PP entity approval#110
Merged
Conversation
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
SEP-10 verify becomes PP-scoped. The wallet posts
ppPublicKeyalongside the signed challenge; the response carries per-PPentityStatus+ 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.status— dropped. Eliminates the dual-write that the old global column would have required.Pipeline changes
requestSchemafor POST/v1/stellar/authrequiresppPublicKey: z.string().min(1).responseSchemacarries{ jwt, entityStatus, kycSubmissionUrl }.P_UpdateChallengeDBreturns the narrowContextWithJWTAndPpPublicKey(justppPublicKey, not the full request body — principle of least information).P_AttachEntityStatuslooks up the per-PP approval row +pp.kyc_submission_urland fills the response shape./v1/entitieswrites/promotes a per-PP approval row (UNVERIFIED → APPROVED) instead of mutatingentity.status.add-bundle.processgates on the per-PP approval row.Test
Full suite green locally (
deno task test: 200 passed).Cross-linked PRs
Test plan
entities.status, addskyc_submission_url+pp_entity_approvals)ppPublicKeyreturns{jwt, entityStatus, kycSubmissionUrl}/v1/entitiescreates a per-PP approval row, not anentity.statuswrite