Skip to content

✨ Referral hook backend — referredBy + invitation.accepted (Node, P8a) #3814

Description

@PierreBrisorgueil

Phase 8a (Node) of the invitations↔org decouple epic. Plan Phase 8a. Referral substrate for #5 — schema/event only, no credit math. Pairs with P8b (Vue, #4282).

⚠️ E14 — add the schema first, else the field is silently dropped/rejected.

  • modules/users/models/users.model.mongoose.js — add referredBy: { type: ObjectId, ref:'User', default:null } (index if queried). users.schema.js (Zod) allows it. Server-set only (from invitation.invitedBy) — keep OUT of the client signup whitelist (safeBody).
  • ⚠️ E20 — block referredBy on EVERY user-update path (Phase-0 audit): rg the user-update controllers/repos (PATCH /api/users/me, admin user-update, profile update) and ensure referredBy is NOT in their writable whitelist (strip it). Test: PATCH /api/users/me with referredBy ignored. Not just the signup whitelist.
  • ⚠️ E22 — shared finalize helper used by BOTH the token two-phase path AND the OAuth path (checkOAuthUserProfile via findValidByEmail): set referredBy = invitation.invitedBy on the created user + acceptedUserId on the invitation, then invitationEvents.emit('invitation.accepted', { invitationId, email, invitedBy, acceptedUserId }). Without it, OAuth-invited users aren't credited (adapting to front #5 gap).
  • modules/billing/billing.init.jsinvitationEvents.on('invitation.accepted', …) listener = no-op + // TODO(#5): grant credits.
  • modules/invitations/lib/events.js doc-block — finalize the payload.

Acceptance: referredBy persists server-side on accept (token AND OAuth paths); client-supplied referredBy in signup body AND PATCH /api/users/me ignored; event emitted; billing listener fires (spy) no-op. Zero credit-grant logic.


🛑 Fable review corrections (2026-06-10, code-verified vs origin/master)

  • E20 endpoint corrected: there is NO PATCH /api/users/me — it's PUT /api/users with a service-level whitelist config.whitelists.users.update (+ updateAdmin) in users.development.config.js. referredBy is blocked by simply NOT adding it to those arrays; the deliverable is the negative test (PUT /api/users + admin update with referredBy → ignored), not a strip.
  • File separately (pre-existing hole): email IS in the update whitelist and changing it does NOT reset emailVerified → a verified user swaps to an unverified email keeping emailVerified:true, trusted by OAuth linkProviderByEmail. Open an ERRORS.md/issue; out of scope here.
  • E22 shared finalize helper (token + OAuth both set referredBy) confirmed correct and necessary.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions