Four bearer-secret-gated server-to-server endpoints that mirror JSN
supporters into cmlite. All share a constant-time `requireBridgeSecret`
(timingSafeEqual on `Bearer ${JSN_BRIDGE_SECRET}`) and per-route
Redis-backed rate limiting.
- POST /api/auth/bridge/users — identity provisioning, idempotent on email.
Tags users with `permissions._jsnProvenance` (jsn-funnel | jsn-donate |
jsn-organize | jsn-organic) and createdVia=JSN_BRIDGE. On new-user
creation, fire-and-forget RC account provisioning via the existing
rocketchatClient (createUser with throwaway password; SSO uses admin-side
createUserToken). Stores RC user id on User.rocketChatUserId.
- POST /api/payments/bridge/record — donation mirror, idempotent on
stripePaymentIntentId. Creates Invoice (type='donation', status='paid')
+ Payment in one transaction. 404s if no cmlite user for the email
(JSN provisions first).
- POST /api/auth/bridge/social-links — handle sync, mirror semantics
keyed on (userId, platform). Mirror per the existing handoff spec at
docs/docs/handoff/cmlite-bridge.md on the JSN side.
- POST /api/rocketchat/bridge/auth — returns RC SSO token by looking up
User.rocketChatUserId and calling rocketchatClient.createUserToken.
Returns {enabled: false} when chat is off or the user has no RC account.
env.ts gains JSN_BRIDGE_SECRET (z.string().min(32).optional()).
Prisma migrations:
- add_jsn_bridge_to_user_created_via — enum value JSN_BRIDGE
- extend_social_platforms_and_unique — facebook/threads/bluesky/linkedin +
composite unique (user_id, platform)
- add_user_rocketchat_user_id — User.rocketChatUserId column
JSN-side spec lives in the JSN repo at
docs/docs/integrations/changemaker.md.
Bunker Admin