dub icon indicating copy to clipboard operation
dub copied to clipboard

Fingerprint integration

Open devkiran opened this issue 3 months ago • 3 comments

Summary by CodeRabbit

Release Notes

  • New Features

    • Added device fingerprinting for enhanced partner identity verification during onboarding
    • Introduced geolocation mismatch detection in fraud risk assessment
    • Added duplicate account detection to prevent fraudulent activity
  • Chores

    • Restricted advanced fraud risk assessment features to Advanced and Enterprise plans
    • Standardized inactive partner enrollment status management

✏️ Tip: You can customize this high-level summary in your review settings.

devkiran avatar Nov 26 '25 14:11 devkiran

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Updated (UTC)
dub Error Error Dec 2, 2025 6:11pm

vercel[bot] avatar Nov 26 '25 14:11 vercel[bot]

Walkthrough

This PR introduces fingerprint-based device identification for partners during onboarding and login, refactors fraud risk detection with new device- and geolocation-based rules, consolidates hardcoded enrollment status arrays into shared constants, and adds plan-based gating to risk evaluation endpoints.

Changes

Cohort / File(s) Change Summary
Fingerprint Integration (Provider & Components)
apps/web/app/(ee)/partners.dub.co/fingerprint-provider.tsx, apps/web/app/(ee)/partners.dub.co/(auth)/login/partner-login-form.tsx, apps/web/app/(ee)/partners.dub.co/(auth)/login/page.tsx, apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/page.tsx
New FingerprintProvider wraps login and onboarding forms with FingerprintJS Pro context; new PartnerLoginForm component replaces LoginForm on login page; onboarding page wrapped with FingerprintProvider.
Fingerprint Data Storage & Fetching
apps/web/lib/api/fraud/fingerprint.ts, apps/web/lib/actions/partners/onboard-partner.ts, apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx
New fetchVisitorFingerprint utility validates and retrieves visitor ID and country from Fingerprint API; onboarding flow integrates visitorId/visitorCountry into partner payload; form captures requestId and propagates visitor data.
Risk Detection Refactoring
apps/web/lib/api/fraud/get-application-risk-signals.ts, apps/web/lib/api/fraud/get-partner-high-risk-signals.ts, apps/web/lib/api/fraud/rules/check-partner-country-mismatch.ts
New getApplicationRiskSignals helper replaces getPartnerHighRiskSignals with comprehensive risk signal mapping; old file removed; new country-mismatch fraud check added.
Fraud Rules & Types
apps/web/lib/api/fraud/constants.ts, apps/web/lib/types.ts
Email-domain mismatch rule replaced with duplicate-account detection (severity: low → high); masked-email rule replaced with geolocation-mismatch rule (severity: low → medium); legacy rules re-added as separate entries; ExtendedFraudRuleType updated with new rule types.
Enrollment Status Constants
apps/web/lib/zod/schemas/partners.ts, apps/web/app/(ee)/api/embed/referrals/links/[linkId]/route.ts, apps/web/app/(ee)/api/partner-profile/programs/[programId]/links/route.ts, apps/web/app/(ee)/api/stripe/connect/webhook/account-updated.ts, apps/web/lib/actions/partners/reject-partner-application.ts, apps/web/ui/partners/partner-info-cards.tsx
New INACTIVE_PROGRAM_ENROLLMENT_STATUSES constant exported from partners schema; replaces hardcoded ["banned", "deactivated", "rejected"] arrays across multiple files.
Auto-Approval & Risk Gating
apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts
Replaced getPartnerHighRiskSignals with getApplicationRiskSignals; added plan-capability gating via getPlanCapabilities; conditional risk evaluation only when canManageFraudEvents is true; skip auto-approval if risk severity is "high".
Risk Endpoint Gating
apps/web/app/(ee)/api/partners/[partnerId]/application-risks/route.ts
GET handler now wrapped with requiredPlan constraint to ["advanced", "enterprise"]; simplified response using getApplicationRiskSignals; removed per-rule validators and intermediate aggregation logic.
Risk UI Updates
apps/web/ui/partners/fraud-risks/partner-application-risk-summary.tsx
PartnerApplicationRiskSummaryUpsell signature changed to parameterless function with hard-coded severity "high"; severity prop removed from call site; overlay styling refactored.
Database Schema
packages/prisma/schema/partner.prisma
Added optional visitorId and visitorCountry fields to Partner model; added index on visitorId.
Onboarding Schema & Env
apps/web/lib/zod/schemas/partners.ts, apps/web/.env.example
onboardPartnerSchema extended with required requestId field; environment variables added: NEXT_PUBLIC_FINGERPRINT_PUBLIC_KEY and FINGERPRINT_SECRET_KEY.
Dependencies
apps/web/package.json
Added @fingerprintjs/fingerprintjs-pro-react@^2.7.1 as runtime dependency.

Sequence Diagram(s)

sequenceDiagram
    actor Partner
    participant LoginPage as Login Page
    participant FingerprintJS as FingerprintJS Pro
    participant Backend as Backend API
    participant DB as Database
    
    Partner->>LoginPage: Visit partner login
    LoginPage->>FingerprintJS: Initialize FingerprintProvider
    FingerprintJS->>FingerprintJS: Generate device fingerprint
    Partner->>LoginPage: Submit login (email/password)
    LoginPage->>Backend: POST /auth/login with requestId
    Backend->>DB: Fetch partner by email
    DB-->>Backend: Partner record (if exists)
    Backend->>Backend: Generate session token
    Backend-->>LoginPage: Redirect to dashboard
    
    Partner->>Partner: Navigate to onboarding
    participant OnboardingPage as Onboarding Page
    Partner->>OnboardingPage: View onboarding form
    OnboardingPage->>FingerprintJS: Get visitor data
    FingerprintJS-->>OnboardingPage: visitorId, visitorCountry
    OnboardingPage->>OnboardingPage: Store in form state
    Partner->>OnboardingPage: Submit onboarding form
    OnboardingPage->>Backend: POST /onboard-partner with requestId
    Backend->>Backend: fetchVisitorFingerprint(requestId)
    Backend->>FingerprintJS: Query Fingerprint API for visitor data
    FingerprintJS-->>Backend: visitorId, visitorCountry
    Backend->>DB: Store Partner with fingerprint data
    Backend->>Backend: getApplicationRiskSignals(program, partner)
    Backend->>DB: Query for cross-program bans, duplicates
    DB-->>Backend: Risk signal results
    Backend->>Backend: Compute risk severity
    Backend-->>OnboardingPage: Success with risk assessment
sequenceDiagram
    participant Cron as Auto-Approve Cron
    participant API as Backend API
    participant DB as Database
    participant FraudAPI as Fraud Detection
    
    Cron->>API: Trigger auto-approve-partner
    API->>DB: Fetch pending program enrollments
    DB-->>API: List of pending enrollments
    loop For each enrollment
        API->>API: getPlanCapabilities(workspace)
        alt canManageFraudEvents = true
            API->>FraudAPI: getApplicationRiskSignals(program, partner)
            FraudAPI->>DB: Query fraud indicators
            DB-->>FraudAPI: Risk data
            FraudAPI-->>API: riskSignals, severity
            alt severity = "high"
                API->>API: Skip auto-approval, log warning
            else
                API->>DB: Update enrollment to approved
                DB-->>API: Success
            end
        else
            API->>DB: Auto-approve (no fraud checks)
            DB-->>API: Success
        end
    end

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Areas requiring extra attention:

  • Fraud rule refactoring (lib/api/fraud/constants.ts, lib/api/fraud/get-application-risk-signals.ts): Verify that new risk signal logic correctly identifies all previously-detected fraud cases; ensure severity mapping aligns with intended gating behavior (e.g., "high" severity blocks auto-approval).
  • Fingerprint API integration (lib/api/fraud/fingerprint.ts, lib/actions/partners/onboard-partner.ts): Confirm error handling doesn't block onboarding if Fingerprint API is unavailable; validate requestId threading through form → action → database.
  • Database schema changes (packages/prisma/schema/partner.prisma): Verify migration strategy for adding optional visitorId/visitorCountry fields and index; confirm no existing data or queries break.
  • Plan-capability gating (app/(ee)/api/cron/auto-approve-partner/route.ts, app/(ee)/api/partners/[partnerId]/application-risks/route.ts): Ensure getPlanCapabilities is called correctly and that requiredPlan constraint on the GET endpoint blocks non-qualifying plans as intended.
  • Enrollment status constant rollout (six files updating to use INACTIVE_PROGRAM_ENROLLMENT_STATUSES): Verify all hardcoded status arrays were replaced and no edge cases missed; confirm constant value matches previous literals.

Possibly related PRs

  • dubinc/dub#2697: Both use getPlanCapabilities for plan-based feature gating (rewards upsell vs. fraud risk evaluation).
  • dubinc/dub#3153: Both refactor fraud detection surface, including risk-signal helpers and route/consumer changes (getApplicationRiskSignals vs. getPartnerHighRiskSignals).
  • dubinc/dub#2555: Modifies the same auto-approve-partner cron route, updating its risk-check implementation.

Suggested reviewers

  • steven-tey

🐰 Hop, hop—now we've got fingerprints to spare,
Device IDs floating through the network air,
Duplicate accounts beware, we've found your scheme,
Geolocation checks fulfill our fraud-free dream,
With plans in place and risks now clear—
Partner safety reigns supreme here!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fingerprint integration' accurately and concisely summarizes the main change in the changeset, which introduces Fingerprint device identification across partner authentication and onboarding flows.
✨ Finishing touches
  • [ ] 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • [ ] Create PR with unit tests
  • [ ] Post copyable unit tests in a comment
  • [ ] Commit unit tests in branch fraud-fingerprint

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b7acff8746fe54ff109eee81b43f9d226fcb6df2 and af66903e44a2d8cc45ecc0da1e31146e367c94c2.

📒 Files selected for processing (2)
  • apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts (4 hunks)
  • apps/web/ui/partners/fraud-risks/partner-application-risk-summary.tsx (4 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-11-24T09:10:12.536Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/lib/api/fraud/fraud-rules-registry.ts:17-25
Timestamp: 2025-11-24T09:10:12.536Z
Learning: In apps/web/lib/api/fraud/fraud-rules-registry.ts, the fraud rules `partnerCrossProgramBan` and `partnerDuplicatePayoutMethod` intentionally have stub implementations that return `{ triggered: false }` because they are partner-scoped rules handled separately during partner application/onboarding flows (e.g., in detect-record-fraud-application.ts), rather than being evaluated per conversion event like other rules in the registry. The stubs exist only to satisfy the `Record<FraudRuleType, ...>` type constraint.

Applied to files:

  • apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts
  • apps/web/ui/partners/fraud-risks/partner-application-risk-summary.tsx
📚 Learning: 2025-11-12T22:23:10.414Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 3098
File: apps/web/lib/actions/partners/message-program.ts:49-58
Timestamp: 2025-11-12T22:23:10.414Z
Learning: In apps/web/lib/actions/partners/message-program.ts, when checking if a partner can continue messaging after messaging is disabled, the code intentionally requires `senderPartnerId: null` (program-initiated messages) to prevent partners from continuing to send junk messages. Only conversations started by the program should continue after messaging is disabled, as a spam prevention mechanism.

Applied to files:

  • apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts
📚 Learning: 2025-08-16T11:14:00.667Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2754
File: apps/web/lib/partnerstack/schemas.ts:47-52
Timestamp: 2025-08-16T11:14:00.667Z
Learning: The PartnerStack API always includes the `group` field in partner responses, so the schema should use `.nullable()` rather than `.nullish()` since the field is never omitted/undefined.

Applied to files:

  • apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts
📚 Learning: 2025-10-15T01:52:37.048Z
Learnt from: steven-tey
Repo: dubinc/dub PR: 2958
File: apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/members/page-client.tsx:270-303
Timestamp: 2025-10-15T01:52:37.048Z
Learning: In React components with dropdowns or form controls that show modals for confirmation (e.g., role selection, delete confirmation), local state should be reverted to match the prop value when the modal is cancelled. This prevents the UI from showing an unconfirmed change. The solution is to either: (1) pass an onClose callback to the modal that resets the local state, or (2) observe modal visibility state and reset on close. Example context: RoleCell component in apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/members/page-client.tsx where role dropdown should revert to user.role when UpdateUserModal is cancelled.

Applied to files:

  • apps/web/ui/partners/fraud-risks/partner-application-risk-summary.tsx
🧬 Code graph analysis (1)
apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts (2)
apps/web/lib/plan-capabilities.ts (1)
  • getPlanCapabilities (4-22)
apps/web/lib/api/fraud/get-application-risk-signals.ts (1)
  • getApplicationRiskSignals (12-85)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (7)
apps/web/ui/partners/fraud-risks/partner-application-risk-summary.tsx (3)

40-40: LGTM - consistent with component refactor.

The removal of the severity prop is consistent with the refactored PartnerApplicationRiskSummaryUpsell component that no longer accepts this parameter. This makes sense for the upsell view, where actual risk data should not be displayed to users without access.


156-161: LGTM - improved upsell overlay structure.

The UI restructuring improves the upsell overlay by:

  • Adding a dedicated non-blurred header area for better readability
  • Using proper layering with z-index to position the header above the blurred content
  • Maintaining visual hierarchy with the negative margins

The removal of backdrop-blur and the new header container enhance the visual clarity of the upsell message.


175-176: LGTM - improved clarity.

The text update improves grammatical flow by explicitly stating the features are "available on the Advanced plan" rather than just "Advanced plan".

apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts (4)

2-2: LGTM!

Clean addition of required imports for the new risk evaluation and plan capability features.

Also applies to: 5-5


68-74: LGTM!

The explicit null check for partner correctly addresses previous review feedback and ensures safe access to partner data in subsequent operations. The check ordering is now correct with programEnrollment validated first.


54-54: I'll verify that the Partner model includes all required fields for the risk evaluation. Let me search the codebase to confirm this. <function_calls> #!/bin/bash

Find the Prisma schema file

fd -t f "schema.prisma" --exec cat {} ; | head -200 </function_calls>

Executing...

<function_calls> #!/bin/bash

Search for Partner model definition in the repository

rg "model Partner" -A 60 --type prisma </function_calls>

Executing...

<function_calls> #!/bin/bash

Find where getApplicationRiskSignals is defined to see what fields it requires

rg -n "getApplicationRiskSignals" --type ts --type tsx -A 20 </function_calls>

Executing...


96-113: Design decisions verified as intentional and consistent.

After thorough verification of the codebase:

  1. Plan gating (enterprise/advanced only): Confirmed intentional. canManageFraudEvents is explicitly gated in getPlanCapabilities to only ["enterprise", "advanced"] plans, consistent with other premium features. Lower-tier plans bypass fraud checks during auto-approval.

  2. Severity threshold (only "high" blocks): Confirmed intentional. The code explicitly checks if (severity === "high") with clear logging. Multiple fraud rules are evaluated across high, medium, and low severity levels, but only the highest severity of "high" triggers the auto-approval block.

Both design decisions are well-documented through clear code comments and follow established patterns throughout the codebase. No TODOs or FIXMEs indicate incomplete work.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot] avatar Nov 26 '25 14:11 coderabbitai[bot]

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​fingerprintjs/​fingerprintjs-pro-react@​2.7.19210010083100

View full report

socket-security[bot] avatar Dec 02 '25 18:12 socket-security[bot]