fix(j0di3): Capture and forward X-Troop-Token on troop-scoped calls#1136
Merged
Conversation
J0dI3 now requires X-Troop-Token (per-troop session token) on every
troop-scoped resource alongside the existing X-API-Key. Without it,
every /api/j0di3/* call returns 403 with TROOP_ACCESS_DENIED.
- Schema: add User.troopAccessToken with migration.
- ensureTroop: capture access_token from POST /troops/ registration.
For existing users with troopId but no token, rotate one via
POST /troops/{id}/access-token/rotate (admin-scoped, uses our API
key) on next session callback — transparent self-heal.
- requireAuth: load troopAccessToken from DB, expose as
req.user.troopToken.
- j0di3Proxy dispatch: inject X-Troop-Token header on troop-scoped
calls; return 400 with a re-auth hint if the token is missing.
j0di3AdminProxy skips the header — admin scope bypasses the check.
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Production is returning 403
TROOP_ACCESS_DENIEDon every J0dI3 call because the backend added a per-troop session header —X-Troop-Token— that our proxy doesn't send. This PR captures the token at troop registration, backfills it for existing users, and forwards it on every troop-scoped request.Root cause
The J0dI3 backend's
_check_troop_accessmiddleware now requires two headers on troop-scoped resources:X-API-Key(unchanged) — service identity, already sent.X-Troop-Token(new) — per-user troop session, missing.Admin-scoped clients skip the check, which is why admin endpoints aren't affected.
The token is minted once on
POST /api/v1/troops/(returned asaccess_token) and can be re-minted viaPOST /api/v1/troops/{troop_id}/access-token/rotate(admin scope).Fix
prisma/schema.prisma+ migration) — addsUser.troopAccessToken String?.ensureTroop(src/lib/ensure-troop.ts) — capturesaccess_tokenon new registration. For existing users withtroopIdbut no token, calls the admin rotate endpoint once and persists. Readsaccess_token/troop_access_token/tokendefensively in case backend naming differs.requireAuth(src/lib/rbac.ts) — loadstroopAccessTokenfrom DB and exposes asreq.user.troopToken.j0di3Proxydispatch (src/lib/j0di3-proxy.ts) — injectsX-Troop-Tokenon troop-scoped calls. Returns 400 with a re-auth hint if the token is missing.j0di3AdminProxyskips the header.Backfill plan
ensureTroopis fired from the NextAuth session callback (src/pages/api/auth/options.ts:125) on every session refresh, so existing users self-heal transparently — no manual migration script, no forced sign-out. Worst case: a user's first J0dI3 call after deploy still 400s; the next session refresh rotates a token and subsequent calls succeed.Verification
npm test— 382/382 passing (added 2 new tests: rotate-on-backfill, missing-token 400 path)npm run lint— clean on touched filesnpm run typecheck— only the same pre-existing errors as master (swagger / 4 test files)Test plan
npx prisma migrate deployadds thetroopAccessTokencolumntroopIdbut notroopAccessToken→ next request to/api/j0di3/troops/dashboardsucceeds (after session refresh firesensureTroop→ rotate)ensureTroopregisters and stores token in the same Prisma update/api/j0di3/troops/dashboardrequest hasX-Troop-Tokenheader in upstream call (check backend logs or proxy with a probe)/api/j0di3/admin/*calls do NOT sendX-Troop-Token(admin scope)dispatch) — user can sign out and back in to re-rotateNotes
X-API-Keystays). The 403 was purely about the missing troop token.ensureTroopfield-name fallback (access_token || troop_access_token || token) is defensive — drop to justaccess_tokenonce the backend response shape is confirmed.