Fingerprint integration
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.
The latest updates on your projects. Learn more about Vercel for GitHub.
| Project | Deployment | Preview | Updated (UTC) |
|---|---|---|---|
| dub | Dec 2, 2025 6:11pm |
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
severityprop is consistent with the refactoredPartnerApplicationRiskSummaryUpsellcomponent 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-blurand 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
partnercorrectly addresses previous review feedback and ensures safe access to partner data in subsequent operations. The check ordering is now correct withprogramEnrollmentvalidated 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:
Plan gating (enterprise/advanced only): Confirmed intentional.
canManageFraudEventsis explicitly gated ingetPlanCapabilitiesto only["enterprise", "advanced"]plans, consistent with other premium features. Lower-tier plans bypass fraud checks during auto-approval.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.
Comment @coderabbitai help to get the list of available commands and usage tips.
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
| Diff | Package | Supply Chain Security |
Vulnerability | Quality | Maintenance | License |
|---|---|---|---|---|---|---|
| @fingerprintjs/fingerprintjs-pro-react@2.7.1 |