Skip to content

wave: require KYC to apply for issues + KYC prompt step#1906

Merged
efstajas merged 10 commits into
mainfrom
feat/require-kyc-for-applications
May 21, 2026
Merged

wave: require KYC to apply for issues + KYC prompt step#1906
efstajas merged 10 commits into
mainfrom
feat/require-kyc-for-applications

Conversation

@efstajas
Copy link
Copy Markdown
Contributor

@efstajas efstajas commented May 20, 2026

Summary

Frontend companion to drips-network/wave#596 for drips-network/wave#592. Pushes KYC earlier in the contributor funnel: blocks the apply page when the user is not verified, and surfaces a dedicated "Verify your identity" step once after login.

  • New /wave/kyc-required step: yellow warning AnnotationBox + primary "Verify identity" / ghost "Skip for now" buttons. Reachable only from the post-login redirect (and direct nav). Self-resolves if the user is already verified.
  • Login callback: after login, redirects non-verified users to /wave/kyc-required. RED-reviewed (rejected) users skip the prompt — the "please verify your identity" pitch is misleading for them.
  • Apply page:
    • apply/+layout.ts fetches KYC status and returns isKycVerified. No redirect — the apply page is reachable, just blocked.
    • apply/+page.svelte shows a red Identity verification (KYC) required AnnotationBox with a "Verify identity" action; both FormFields (application text + Turnstile) get disabled so the wrapper greys out and ignores input; the application-slots info box is hidden in this state. Backend 403 still guards submission.
  • Settings/profile: tiny copy tweak on the existing KYC nudge to align with the new gate framing.
  • Support page: unrelated one-line response-time copy tweak.

Test plan

  • Logged out → log in as a user without KYC → expect /wave/kyc-required after the callback (wrapping the welcome flow for new users).
  • Same flow as a RED-reviewed user → expect direct redirect to backTo, no kyc-required step.
  • Navigate directly to /wave/<slug>/issues/<id>/apply without KYC → expect the red KYC AnnotationBox, disabled form, no slots info box; "Verify identity" button links to /wave/kyc?backTo=….
  • Complete KYC, return to the apply page → form renders live, slots AnnotationBox returns, submit works.
  • On /wave/kyc-required: "Skip for now" lands on backTo; "Verify identity" lands on /wave/kyc?backTo=….
  • Visit /wave/kyc-required directly while already verified → expect redirect to backTo.

Note

The "Learn more" link in the warning AnnotationBox points to docs.drips.network/wave/contributors/solving-issues-and-earning-rewards#verifying-your-identity — that docs section needs to be authored before merge / shortly after.

Companion to the backend gate. Block the apply page when the user lacks
verified KYC, and add a dedicated "Verify your identity" step that's
surfaced once after login.

- New /wave/kyc-required step with warning AnnotationBox and Verify /
  Skip buttons, shown after login for users without verified KYC. RED
  (rejected) users skip the prompt — the pitch is misleading for them.
- apply page: fetches KYC status and disables the form (FormField
  `disabled`), hides the slots AnnotationBox, and shows a red blocker
  with a Verify identity action when not verified. Backend 403 still
  guards submission.
- Profile settings KYC nudge copy aligned with the new gate framing.
- Minor support page response-time copy tweak.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a frontend KYC gate earlier in the Wave contributor funnel by introducing a post-login “KYC required” prompt and disabling issue applications for users without verified identity.

Changes:

  • Redirects newly-logged-in, non-verified users to a new /wave/kyc-required step (with a “verify” CTA and a “skip” option).
  • Fetches KYC status on the issue apply route and blocks the apply form UI when the user is not KYC-verified.
  • Updates small bits of copy on the support page and the profile settings KYC nudge.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/routes/(pages)/wave/(flows)/support/+page.svelte Updates support response-time copy.
src/routes/(pages)/wave/(flows)/login/callback/+page.svelte Adds post-login KYC status fetch + redirect to /wave/kyc-required for non-verified users.
src/routes/(pages)/wave/(flows)/kyc-required/+page.ts Implements server load: validates backTo, fetches KYC status, redirects if already verified, otherwise requires auth.
src/routes/(pages)/wave/(flows)/kyc-required/+page.svelte New UI step prompting identity verification with “Verify identity” / “Skip for now”.
src/routes/(pages)/wave/(flows)/[waveProgramSlug]/issues/[issueId]/apply/+layout.ts Adds KYC status fetch and computes isKycVerified for the apply page.
src/routes/(pages)/wave/(flows)/[waveProgramSlug]/issues/[issueId]/apply/+page.svelte Blocks apply UI when not KYC-verified; adds KYC-required AnnotationBox and disables form + submit.
src/routes/(pages)/wave/(base-layout)/settings/profile/+page.svelte Adjusts KYC nudge copy to match new gating framing.
Comments suppressed due to low confidence (1)

src/routes/(pages)/wave/(flows)/[waveProgramSlug]/issues/[issueId]/apply/+page.svelte:203

  • Even when the form is "disabled" via the FormField wrapper, the Turnstile component still mounts and runs its onMount logic (polling for the script + rendering the widget). For non-KYC users this is wasted work and may still create network/activity. Consider conditionally rendering the Turnstile (and/or the whole verification FormField) only when data.isKycVerified is true.
    <FormField
      title="Verification*"
      description="Confirm you're not a robot with a quick check."
      type="div"
      disabled={!data.isKycVerified}
    >
      <div class="turnstile-wrapper">
        <Turnstile ontoken={(t) => (turnstileToken = t)} />
      </div>
    </FormField>

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/routes/(pages)/wave/(flows)/login/callback/+page.svelte Outdated
Comment thread src/routes/(pages)/wave/(flows)/kyc-required/+page.ts
efstajas added 4 commits May 21, 2026 10:30
Previously a transient failure on /api/kyc/status would silently coerce
isKycVerified to false, blocking already-verified users with a misleading
"please verify" banner. The call is rare and unexpected to fail, so we
let it bubble to SvelteKit's error boundary instead.
Wrap the post-login getKycStatus() call in try/catch and proceed with
the normal welcome/backTo flow on failure instead of throwing the user
into the generic login-error state. The kyc-required nudge is a
best-effort hint, not a login prerequisite.
Mirrors the login callback's behavior: a user whose KYC was rejected
shouldn't see the "please verify your identity" pitch when they land
on /wave/kyc-required directly (back button, stale bookmark, etc).
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

src/routes/(pages)/wave/(flows)/kyc-required/+page.ts:27

  • The catch redirects to /wave/login for any error from getKycStatus(). If the user is already logged in (JWT present) but the KYC status endpoint errors (5xx/network), /wave/login immediately redirects back to backTo (this page), creating an infinite redirect loop. Consider checking parent() for user and only redirecting to login when unauthenticated/401; for other errors, render the page (best-effort) or redirect to safeBackTo instead of login.
  try {
    const kycStatus = await getKycStatus(fetch);
    const isKycVerified =
      kycStatus.status === 'applicantReviewed' && kycStatus.reviewAnswer === 'GREEN';
    // RED users have already attempted KYC; the "please verify" pitch on this
    // page is misleading for them. Mirror the login callback's behavior.
    const isKycRejected = kycStatus.reviewAnswer === 'RED';

    if (isKycVerified || isKycRejected) {
      throw redirect(302, safeBackTo);
    }
  } catch (err) {
    if (err && typeof err === 'object' && 'status' in err && err.status === 302) {
      throw err;
    }
    // If not authenticated or other error, redirect to login
    throw redirect(302, `/wave/login?backTo=${encodeURIComponent(url.pathname + url.search)}`);
  }

Comment thread src/routes/(pages)/wave/(flows)/kyc-required/+page.ts Outdated
Comment thread src/routes/(pages)/wave/(flows)/kyc-required/+page.svelte
url.searchParams.get() already returns the decoded value, so a second
decodeURIComponent would either double-decode (turning encoded slashes
back into path separators) or throw on a literal % in the input.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

src/routes/(pages)/wave/(flows)/kyc-required/+page.ts:26

  • The catch-all error handler redirects to /wave/login for any failure (including transient backend/network errors while the user is already authenticated). This can cause confusing login loops and hides real failures. Consider only redirecting to login on 401/unauthenticated, and otherwise either allow rendering the page (best-effort) or surface an error state.
  } catch (err) {
    if (err && typeof err === 'object' && 'status' in err && err.status === 302) {
      throw err;
    }
    // If not authenticated or other error, redirect to login
    throw redirect(302, `/wave/login?backTo=${encodeURIComponent(url.pathname + url.search)}`);
  }

Comment thread src/routes/(pages)/wave/(flows)/login/callback/+page.svelte
Comment thread src/routes/(pages)/wave/(flows)/kyc-required/+page.ts Outdated
Comment thread src/routes/(pages)/wave/(flows)/kyc-required/+page.svelte
efstajas added 2 commits May 21, 2026 11:31
If a user is bounced from /wave/kyc-required through /wave/login and
back, the backTo arriving at the login callback already points at
kyc-required. Wrapping it again produced /wave/kyc-required?backTo=
%2Fwave%2Fkyc-required%3F..., which would land Skip-for-now back on
kyc-required and grow the URL on every login attempt. Extract the
inner backTo before wrapping (capped to avoid pathological deep nests).
Aligns backTo parsing with /wave/login and /wave/verify-phone instead
of reimplementing the decode + safety dance inline.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Comment thread src/routes/(pages)/wave/(flows)/kyc-required/+page.ts Outdated
efstajas added 2 commits May 21, 2026 12:19
Previously any error from getKycStatus (5xx, network, parsing) would
bounce the user to /wave/login, which masks real outages and can cause
loops for already-authenticated users. Restrict the login redirect to
401 only; let everything else propagate to the error boundary.
FormField's disabled prop only added a visual style (opacity +
pointer-events: none on the wrapper) — keyboard users could still tab
into the TextArea and Cloudflare's Turnstile iframe and interact with
them.

- Add a disabled prop to TextArea that passes through to the native
  <textarea>, which properly removes it from the tab order and blocks
  typing.
- Conditionally render <Turnstile> only when KYC is verified so the
  Cloudflare iframe isn't mounted (and can't be focused) until then.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

@efstajas efstajas merged commit 948cde0 into main May 21, 2026
10 of 12 checks passed
@efstajas efstajas deleted the feat/require-kyc-for-applications branch May 21, 2026 14:21
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.

2 participants