Skip to content

rework weights for same name signups#1298

Open
mantrakp04 wants to merge 6 commits intodevfrom
risk-score-rework
Open

rework weights for same name signups#1298
mantrakp04 wants to merge 6 commits intodevfrom
risk-score-rework

Conversation

@mantrakp04
Copy link
Copy Markdown
Collaborator

@mantrakp04 mantrakp04 commented Mar 28, 2026

  • update submodule
  • Enhance sign-up risk assessment by adding sameEmailCount and sameEmailLimit to recent stats request. Update loadRecentSignUpStats function to include email normalization checks. Adjust tests to reflect new return structure.

Summary by CodeRabbit

  • New Features

    • Improved risk scoring to detect and track multiple signup attempts using identical email addresses within a time window, enhancing fraud prevention capabilities.
  • Performance

    • Added database optimization for faster risk assessment queries.

…lLimit to recent stats request. Update loadRecentSignUpStats function to include email normalization checks. Adjust tests to reflect new return structure.
Copilot AI review requested due to automatic review settings March 28, 2026 01:03
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 28, 2026

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

Project Deployment Actions Updated (UTC)
stack-auth-hosted-components Ready Ready Preview, Comment Apr 6, 2026 5:06pm
stack-backend Ready Ready Preview, Comment Apr 6, 2026 5:06pm
stack-dashboard Ready Ready Preview, Comment Apr 6, 2026 5:06pm
stack-demo Ready Ready Preview, Comment Apr 6, 2026 5:06pm
stack-docs Ready Ready Preview, Comment Apr 6, 2026 5:06pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 156092e7-e271-4089-aa7c-a85aeeb57be7

📥 Commits

Reviewing files that changed from the base of the PR and between b57f005 and 526e5eb.

📒 Files selected for processing (1)
  • apps/backend/prisma/migrations/20260406000000_add_signup_email_normalized_recent_idx/migration.sql

📝 Walkthrough

Walkthrough

This PR extends the sign-up risk-scoring system to track duplicate sign-ups by normalized email address. It adds fields to risk-scoring data structures, updates the database schema with a new composite index for efficient querying, creates the corresponding migration, and updates a git submodule reference.

Changes

Cohort / File(s) Summary
Risk Scoring Logic
apps/backend/src/lib/risk-scores.tsx
Extended SignUpRiskRecentStatsRequest with signUpEmailNormalized and sameEmailLimit fields. Extended SignUpRiskRecentStats with sameEmailCount field. Updated loadRecentSignUpStats to compute sameEmailCount via parallel query counting non-anonymous ProjectUser rows matching the normalized email within the recent window. Updated test mocks accordingly.
Database Schema & Migrations
apps/backend/prisma/schema.prisma, apps/backend/prisma/migrations/20260406000000_add_signup_email_normalized_recent_idx/migration.sql
Added composite index ProjectUser_signUpEmailNormalized_recent_idx on (tenancyId, isAnonymous, signUpEmailNormalized, signedUpAt) columns to optimize queries filtering by normalized email and signup recency.
Submodule Reference
apps/backend/src/private/implementation
Updated git submodule commit reference from a93d7ea0 to 576f383b.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested reviewers

  • N2D4

Poem

🐰 A rabbit hops through email fields,
Normalized, deduped, the system yields,
Risk scores computed with indexed grace,
Faster queries now set the pace! 📧✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title mentions 'same name signups' but the actual changes focus on email normalization and sameEmailCount, not name-based matching. Update the title to accurately reflect the main change, such as 'Add same-email signup detection to risk assessment' or 'Enhance risk scoring with email normalization checks'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The description covers the main changes and includes bullet points explaining the updates, though it could be more detailed about the specific database index additions.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch risk-score-rework

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.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 28, 2026

Greptile Summary

This PR enhances the signup risk assessment system by adding same-email detection alongside the existing same-IP and similar-email-base checks. It introduces a sameEmailCount field (matched on the normalized email address) to SignUpRiskRecentStats, a corresponding DB query, a new supporting index, and updates the test mock accordingly.

  • risk-scores.tsx: Extended SignUpRiskRecentStatsRequest with signUpEmailNormalized and sameEmailLimit, added sameEmailCount to SignUpRiskRecentStats, and added a third parallel raw SQL query following the same cost-capped (LIMIT) pattern as the existing IP/email-base queries.
  • schema.prisma + migration.sql: Added a new composite index ProjectUser_signUpEmailNormalized_recent_idx on (tenancyId, isAnonymous, signUpEmailNormalized, signedUpAt) using CREATE INDEX CONCURRENTLY IF NOT EXISTS, consistent with the other fraud-protection indexes added in the same migration file.
  • Tests: The inline mock for loadRecentSignUpStats is updated to include the new sameEmailCount: 0 field, keeping the snapshot in sync.

Confidence Score: 5/5

Safe to merge — changes are additive, consistent with existing patterns, and carry no breaking changes.

All changes follow established patterns (cost-capped LIMIT queries, concurrent IF NOT EXISTS index creation, null-guarding). No logic errors, security issues, or migration safety concerns were found. The test mock is correctly updated. No P1 or P0 findings.

No files require special attention.

Important Files Changed

Filename Overview
apps/backend/src/lib/risk-scores.tsx Adds sameEmailCount detection using signUpEmailNormalized; follows existing patterns for null-guarding, cost-capped LIMIT queries, and Promise.all parallelism. Test mock updated correctly.
apps/backend/prisma/migrations/20260308000002_finalize_signup_fraud_protection/migration.sql New concurrent index on (tenancyId, isAnonymous, signUpEmailNormalized, signedUpAt) added with correct sentinels and IF NOT EXISTS guard, consistent with other indexes in the same migration.
apps/backend/prisma/schema.prisma Prisma index definition added for signUpEmailNormalized, matching the migration SQL exactly.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant calculateSignUpRiskAssessment
    participant loadRecentSignUpStats
    participant DB as DB (replica)

    Caller->>calculateSignUpRiskAssessment: context (email, ip, ...)
    calculateSignUpRiskAssessment->>loadRecentSignUpStats: SignUpRiskRecentStatsRequest
    par same-IP query
        loadRecentSignUpStats->>DB: SELECT 1 WHERE signUpIp = ? LIMIT sameIpLimit
        DB-->>loadRecentSignUpStats: sameIpRows
    and same-normalized-email query (NEW)
        loadRecentSignUpStats->>DB: SELECT 1 WHERE signUpEmailNormalized = ? LIMIT sameEmailLimit
        DB-->>loadRecentSignUpStats: sameEmailRows
    and similar-email-base query
        loadRecentSignUpStats->>DB: SELECT 1 WHERE signUpEmailBase = ? LIMIT similarEmailLimit
        DB-->>loadRecentSignUpStats: similarEmailRows
    end
    loadRecentSignUpStats-->>calculateSignUpRiskAssessment: { sameIpCount, sameEmailCount, similarEmailCount }
    calculateSignUpRiskAssessment-->>Caller: SignUpRiskAssessment (scores + heuristicFacts)
Loading

Reviews (3): Last reviewed commit: "Add index for normalized sign-up email i..." | Re-trigger Greptile

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

Enhances the backend sign-up risk assessment plumbing by extending “recent sign-up stats” to include same-normalized-email counts/limits, and updates dependency lockfile entries likely due to a submodule/dependency refresh.

Changes:

  • Extend recent stats request/response types to include signUpEmailNormalized, sameEmailLimit, and sameEmailCount.
  • Add a DB query to count recent sign-ups matching the same normalized email (bounded by sameEmailLimit).
  • Update the in-source Vitest stubbed dependency return shape to match the new stats structure.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 1 comment.

File Description
pnpm-lock.yaml Updates dependency resolution (notably Nitro now pulling in rolldown) and removes an importer entry.
apps/backend/src/lib/risk-scores.tsx Adds same-normalized-email recent stats fields and query; updates test stub return shape.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

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

@mantrakp04 mantrakp04 requested a review from N2D4 March 28, 2026 01:08
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/backend/src/lib/risk-scores.tsx`:
- Around line 83-93: The query in apps/backend/src/lib/risk-scores.tsx that uses
request.signUpEmailNormalized (inside the prisma.$replica().$queryRaw block that
checks sameEmailLimit) needs a corresponding DB index to avoid full table scans;
add the index to migration 20260308000002_finalize_signup_fraud_protection.sql
(CREATE INDEX CONCURRENTLY IF NOT EXISTS
"ProjectUser_signUpEmailNormalized_recent_idx" ON "ProjectUser"("tenancyId",
"isAnonymous", "signUpEmailNormalized", "signedUpAt")) and add the matching
Prisma index annotation to the ProjectUser model (@@index([tenancyId,
isAnonymous, signUpEmailNormalized, signedUpAt], name:
"ProjectUser_signUpEmailNormalized_recent_idx")) so the signUpEmailNormalized
filter in the sameEmailLimit query can use the index.

In `@apps/backend/src/private/implementation`:
- Line 1: Verify the private submodule update by checking the commit range
between a93d7ea0c0a91d7a4dfbc97c0032c9c9c68ec4d6 and
576f383b69a9593a9cff8d755c64c810aeeae239: run git fetch in the private repo and
git log --oneline a93d7ea..576f383b6 to enumerate commits, then run git diff
--name-status a93d7ea0c..576f383b6 to ensure only the intended risk-score and
signup-stat files were changed (no other files or code paths), inspect each
commit message and diff for breaking/security-impacting edits, run the private
repo’s test suite and any linters/security scanners on the new commits, and if
anything else appears, revert or request the correct commit range before
merging.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1840880f-79f3-4e1f-95d1-fd9e84e37754

📥 Commits

Reviewing files that changed from the base of the PR and between 4ddf6a5 and dadc71f.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (2)
  • apps/backend/src/lib/risk-scores.tsx
  • apps/backend/src/private/implementation

Comment on lines +83 to +93
request.signUpEmailNormalized == null || request.sameEmailLimit === 0
? []
: prisma.$replica().$queryRaw<{ matched: number }[]>`
SELECT 1 AS "matched"
FROM ${sqlQuoteIdent(schema)}."ProjectUser"
WHERE "tenancyId" = ${tenancy.id}::UUID
AND "isAnonymous" = false
AND "signedUpAt" >= ${windowStart}
AND "signUpEmailNormalized" = ${request.signUpEmailNormalized}
LIMIT ${request.sameEmailLimit}
`,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify that no index exists for signUpEmailNormalized

echo "=== Checking Prisma schema for signUpEmailNormalized index ==="
rg -n 'signUpEmailNormalized' apps/backend/prisma/schema.prisma

echo ""
echo "=== Checking migrations for signUpEmailNormalized index ==="
rg -rn 'signUpEmailNormalized' apps/backend/prisma/migrations/

Repository: stack-auth/stack-auth

Length of output: 345


🏁 Script executed:

#!/bin/bash
# Get the full ProjectUser model definition to see all indexes
echo "=== Full ProjectUser model definition ==="
sed -n '/^model ProjectUser/,/^}/p' apps/backend/prisma/schema.prisma | head -100

Repository: stack-auth/stack-auth

Length of output: 4513


🏁 Script executed:

#!/bin/bash
# Read the specific migration file to see what was actually created
echo "=== Checking migration file for signup fraud protection ==="
find apps/backend/prisma/migrations -name "*signup*fraud*" -o -name "*20260308*" | head -5

Repository: stack-auth/stack-auth

Length of output: 478


🏁 Script executed:

#!/bin/bash
# Check for all indexes on ProjectUser in schema
echo "=== All @@index definitions in ProjectUser model ==="
sed -n '/^model ProjectUser/,/^}/p' apps/backend/prisma/schema.prisma | grep -n "@@index"

Repository: stack-auth/stack-auth

Length of output: 1001


🏁 Script executed:

#!/bin/bash
# Read the main signup fraud protection migration
echo "=== Migration 20260308000000 ==="
cat apps/backend/prisma/migrations/20260308000000_add_signup_fraud_protection/migration.sql

Repository: stack-auth/stack-auth

Length of output: 1237


🏁 Script executed:

#!/bin/bash
# Read the finalize migration
echo "=== Migration 20260308000002 ==="
cat apps/backend/prisma/migrations/20260308000002_finalize_signup_fraud_protection/migration.sql

Repository: stack-auth/stack-auth

Length of output: 1879


🏁 Script executed:

#!/bin/bash
# Verify the actual code in risk-scores.tsx around lines 83-93
echo "=== Risk scores query code ==="
sed -n '75,100p' apps/backend/src/lib/risk-scores.tsx

Repository: stack-auth/stack-auth

Length of output: 1157


Add missing database index for signUpEmailNormalized to prevent full table scans.

The query on line 90 filters by signUpEmailNormalized, but no index exists for this column. The sibling queries on signUpIp and signUpEmailBase have corresponding indexes (ProjectUser_signUpIp_recent_idx and ProjectUser_signUpEmailBase_recent_idx), but signUpEmailNormalized was omitted. Without an index, this query will perform a sequential scan on the ProjectUser table, degrading sign-up latency as the table grows.

Add the index to migration 20260308000002_finalize_signup_fraud_protection.sql:

CREATE INDEX CONCURRENTLY IF NOT EXISTS "ProjectUser_signUpEmailNormalized_recent_idx"
  ON "ProjectUser"("tenancyId", "isAnonymous", "signUpEmailNormalized", "signedUpAt");

And add it to the Prisma schema:

@@index([tenancyId, isAnonymous, signUpEmailNormalized, signedUpAt], name: "ProjectUser_signUpEmailNormalized_recent_idx")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/backend/src/lib/risk-scores.tsx` around lines 83 - 93, The query in
apps/backend/src/lib/risk-scores.tsx that uses request.signUpEmailNormalized
(inside the prisma.$replica().$queryRaw block that checks sameEmailLimit) needs
a corresponding DB index to avoid full table scans; add the index to migration
20260308000002_finalize_signup_fraud_protection.sql (CREATE INDEX CONCURRENTLY
IF NOT EXISTS "ProjectUser_signUpEmailNormalized_recent_idx" ON
"ProjectUser"("tenancyId", "isAnonymous", "signUpEmailNormalized",
"signedUpAt")) and add the matching Prisma index annotation to the ProjectUser
model (@@index([tenancyId, isAnonymous, signUpEmailNormalized, signedUpAt],
name: "ProjectUser_signUpEmailNormalized_recent_idx")) so the
signUpEmailNormalized filter in the sameEmailLimit query can use the index.

@@ -1 +1 @@
Subproject commit a93d7ea0c0a91d7a4dfbc97c0032c9c9c68ec4d6
Subproject commit 576f383b69a9593a9cff8d755c64c810aeeae239
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

❓ Verification inconclusive

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify submodule pointer and inspect exact delta
git submodule status -- apps/backend/src/private/implementation || true

# Ensure submodule metadata is available
git submodule update --init --recursive -- apps/backend/src/private/implementation

OLD="a93d7ea0c0a91d7a4dfbc97c0032c9c9c68ec4d6"
NEW="576f383b69a9593a9cff8d755c64c810aeeae239"
SUB="apps/backend/src/private/implementation"

echo "== Commit range in submodule =="
git -C "$SUB" log --oneline --no-merges "${OLD}..${NEW}"

echo
echo "== Files changed in submodule range =="
git -C "$SUB" diff --name-status "${OLD}..${NEW}"

echo
echo "== High-signal grep for risk-score/signup/email changes =="
git -C "$SUB" diff --name-only "${OLD}..${NEW}" | rg -n "risk|signup|sign-up|email|stats|score" || true

Repository: stack-auth/stack-auth

Length of output: 1239


The private submodule cannot be verified from this environment—manual review required.

Line 1 updates the gitlink to commit 576f383b69a9593a9cff8d755c64c810aeeae239, but the private repository (https://github.com/stack-auth/private.git) is not accessible for automated verification. Before merging, confirm that the commit range from a93d7ea0c0a91d7a4dfbc97c0032c9c9c68ec4d6 to 576f383b69a9593a9cff8d755c64c810aeeae239 contains only the intended risk-score and signup-stat changes, with no unrelated breaking or security-impacting updates.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/backend/src/private/implementation` at line 1, Verify the private
submodule update by checking the commit range between
a93d7ea0c0a91d7a4dfbc97c0032c9c9c68ec4d6 and
576f383b69a9593a9cff8d755c64c810aeeae239: run git fetch in the private repo and
git log --oneline a93d7ea..576f383b6 to enumerate commits, then run git diff
--name-status a93d7ea0c..576f383b6 to ensure only the intended risk-score and
signup-stat files were changed (no other files or code paths), inspect each
commit message and diff for breaking/security-impacting edits, run the private
repo’s test suite and any linters/security scanners on the new commits, and if
anything else appears, revert or request the correct commit range before
merging.

- Introduced a new index on the ProjectUser model for the signUpEmailNormalized field, enhancing query performance related to user sign-up activities.
- Updated the migration SQL to create the corresponding index in the database, ensuring efficient data retrieval for analytics and fraud protection measures.
@mantrakp04 mantrakp04 requested a review from N2D4 April 4, 2026 01:26
mantrakp04 and others added 2 commits April 6, 2026 09:42
- Removed the existing index for signUpEmailNormalized from the finalize_signup_fraud_protection migration.
- Created a new migration to add the signUpEmailNormalized index, ensuring better organization and clarity in the migration history.
- This change enhances the database schema by maintaining a clear separation of index creation across migrations.
@mantrakp04 mantrakp04 requested a review from N2D4 April 8, 2026 18:55
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.

3 participants