NXT

Security

Auth model, data collected, GDPR/CCPA posture, deletion, webhook integrity

Auth model

WorkOS AuthKit is the identity provider for the mobile app + web app. The docs site (this site) is public — no auth. Both authenticated surfaces share one WorkOS tenant.

  • Sign-in methods: email + password, magic link, Google OAuth (mobile only today).
  • Sessions: cookie-based on web, JWT-via-secure-store on mobile.
  • Token storage on mobile: expo-secure-store — Keychain on iOS, EncryptedSharedPreferences on Android.
  • Token storage on web: WorkOS AuthKit's encrypted session cookie (WORKOS_COOKIE_PASSWORD encrypts contents).
  • Passwords: NXT does not store them. WorkOS does.

How Convex trusts the session

convex/auth.config.ts registers WorkOS as the identity provider. Convex's userQuery / userMutation / userAction wrapped factories pre-resolve ctx.user from the verified session. Raw query() / mutation() is forbidden by lint — they are publicly callable.

Authorization

Every query that takes a document ID checks ownership before returning:

const doc = await ctx.db.get(id);
if (!doc || doc.userId !== ctx.user._id) throw forbidden();

Forgetting this check = IDOR (insecure direct object reference). The error-factory pattern + the wrapped factories + code review enforce.

Org-scoped resources

Where a resource is shared within an organization (the b2b addon's territory; not actively used by NXT today), the same pattern applies with orgId instead of userId. NXT today is consumer-only, so most checks are user-scoped.

What student data is collected

CategoryFieldSourcePurpose
Identityemail, WorkOS idWorkOS signupaccount
AcademicGPA, SAT, ACT, AP/IBprofile formRFS, matching
Backgroundrace, gender, first-gen statusprofile formMSI rails, peer outcomes
Financefamily income bracket (1 of 5 federal)profile formAfford peek, net-price match
Interestsstudy areas (8 categories)quizPicked For You, Program Leader rails
Personalityquiz result (one of N personas)quizLearning Style rail
Campus settingrural/town/suburb/metro, size, walkability, politicsquizCampus Vibe rail
Storyfree text (achievements, extracurriculars, community service, personal story)profile formprofile depth signal
Savescollege unitId + timestamp + RFS verdict at savesave actionthe user's college list
Quizzesper-question answersquizderive personality + setting

No financial transactions. No payment data. NXT does not bill students.

No conversations. No chat with counselors or peers. No DMs. No messaging at all.

No geo-tracking. Location is the user's self-reported home city/state, never GPS-tracked.

Where data lives

TableWhatEncryption at rest
usersaccount record, WorkOS idConvex-managed
profilesstructured profile fieldsConvex-managed
quiz*quiz answersConvex-managed
savedSchoolssavesConvex-managed
rfsVerdicts / collegeReasoningderived data, cachedConvex-managed
WorkOSpassword hashes, MFA factors, OAuth tokensWorkOS-managed

Convex encrypts data at rest. WorkOS encrypts credentials. No NXT-controlled key escrow.

Webhook integrity

Incoming webhooks (WorkOS user-creation, etc.) are verified by HMAC signature using WORKOS_WEBHOOK_SECRET. The webhook_events table is an idempotency ledger keyed by event ID — duplicate deliveries are no-ops.

If WORKOS_WEBHOOK_SECRET is unset, webhooks are not verified. Today the secret is set.

Account deletion

User-initiated. Triggered from the mobile app (settings) or the web account-deletion page (apps/web/app/(marketing)/delete-account/page.tsx).

The deletion path is a workflow (@convex-dev/workflow):

  1. Mark the user as deletion_pending in users.
  2. Cascade-delete user-scoped rows: profiles, quiz*, savedSchools, rfsVerdicts, collegeReasoning, reports.
  3. Notify WorkOS to delete the underlying identity (idempotent — WorkOS is the source of truth for the auth record).
  4. Notify PostHog via the GDPR erasure path (/api/erase/<distinct_id>) using POSTHOG_PERSONAL_API_KEY.
  5. Mark users row hard-deleted.

The workflow is restartable. If step 3 fails, the workflow retries until WorkOS confirms; the user row stays in deletion_pending until fully cascaded.

Scroll to zoom · drag to pan · Esc to close

Race protection. If a user signs back in mid-deletion, the auth path checks deletion_pending and refuses session creation. No accidental zombie-account.

GDPR / CCPA posture

  • Right to access. A user can download their profile data via the mobile settings page (export-as-JSON action wired but UI flow shallow today).
  • Right to erasure. The account-deletion cascade above. PostHog gets a separate erasure call via API.
  • Data residency. Convex deployment region is configurable. Today: US-East. WorkOS region: US. PostHog region: EU (hosted EU cloud).
  • DPA / SCC. Not signed at the entity level today. If NXT signs an enterprise customer that requires a DPA, the trio of (Convex, WorkOS, PostHog) all offer one.

Content reports

The reports table accepts abuse reports from inside the app (user reports a school, a piece of content, or another user — though users-reporting-users is not a UI flow today since there's no chat).

  • REPORT_ADMIN_EMAIL (optional) is notified on new reports.
  • The dedup query (reports/mutations.ts) filters on (reporterId, _creationTime) over the target's recent reports and takes .first() to prevent spam.

App Store / Play Store reviewer access

REVIEW_LOGIN_EMAILS env var holds the test accounts the App Store and Play Store reviewer teams use. These accounts have full profile data + saves so reviewers can exercise the matching engine without setting up their own.

Don't share these credentials outside the App Store / Play Store flow.

What's NOT in scope

  • HIPAA / PCI / SOC 2. Not pursued. No PHI, no payment data, no enterprise compliance customer yet.
  • Pen-test cadence. No formal pen-test scheduled. Recommended once the user base passes 10K MAU or an enterprise customer with security requirements signs.
  • CSP / SRI on web. Not configured today. Marketing-only surface; risk is low.
  • Mobile binary protection / RASP. Not configured. Standard Hermes JS bundle + Expo bridge.

Risks

  • Single-secret blast radius. Leaking WORKOS_API_KEY or OPENAI_API_KEY is bad but bounded. Mitigation: org-level cost limit on OpenAI, WorkOS audit log monitoring (manual).
  • No secret rotation policy. Open work item — see Credentials.
  • Mobile dev client allows arbitrary JS. Don't push a dev-client build to production.
  • No 2FA enforced on Convex / Vercel / GitHub / WorkOS accounts. Recommended to enable.

Technical detail

Why no end-to-end encryption

Users' profile data is not confidential to NXT — the entire product is built on the user trading data for matches. E2EE would prevent the matching engine from working. The threat model is hostile-actor-accessing-server, not NXT-itself-as-adversary; Convex's at-rest encryption + the auth model address that.

Browsers handle HTTP-only cookies safely; React Native does not have a cookie jar by default. WorkOS AuthKit's mobile SDK uses JWT in Secure Storage as the equivalent.

Why webhook signing matters

WorkOS can also be misconfigured to fire a webhook to your endpoint with a forged event payload. HMAC + the webhook_events ledger means a forged event either fails signature check (rejected) or replays an existing event ID (idempotent no-op).

On this page