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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions