Conversation
…erId The app trusted client-supplied user identifiers in several places, allowing any caller to read/write another user's private notes and transcripts (IDOR). - WS sync (/ws/sync): upgraded on a raw ?userId= query param with no auth. Now requires a MentraOS frontend token (?aos_frontend_token= or Bearer), verified by regenerating the SDK token and constant-time comparing; the userId is derived from the verified token. Rejects with 401 otherwise. - Search endpoints (/api/search, /search/sentences, /search/backfill-status): removed the explicit "|| query userId" auth bypass; identity now comes only from the verified auth middleware (requireAuth). - Dev test endpoints (/api/test/simulate-*): returned data for an arbitrary user with no auth and shipped to prod. Now 404 unless NODE_ENV=development. - Removed dead useSSE hook that hit an unimplemented /api/events?userId= route (misleading insecure pattern; nothing rendered it). Frontend: WS sync and search now authenticate with the verified frontendToken from useMentraAuth() instead of sending a raw userId. useSynced reads the token internally so call sites are unchanged. Verified: token verification logic unit-checked against the real SDK (valid tokens accepted; forged/tampered/wrong-key/empty rejected). No new type errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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
Fixes the same IDOR class as the Livestreamer disclosure, found across several endpoints in Notes. The app trusted client-supplied user identifiers, letting any caller read/write another user's private notes and live voice transcripts.
Issues fixed
WS /ws/sync?userId=with no auth — the primary sync channel for all notes/transcripts?aos_frontend_token=orBearer); userId derived from it; 401 otherwiseGET /api/search,/search/sentences,/search/backfill-status|| query("userId")auth bypassrequireAuth(c)(verified middleware)POST /api/test/simulate-*NODE_ENV=developmentuseSSE→/api/events?userId=How identity is verified
A frontend token is
userId:sha256(userId + sha256(apiKey)). The WS upgrade splits out the claimed userId, regenerates the token via the SDK's owngenerateFrontendToken(userId, apiKey), and constant-time compares. Only a token signed with our API key validates — no network round-trip per connect.Frontend: WS sync and search authenticate with the verified
frontendTokenfromuseMentraAuth().useSyncedreads the token internally, so the other 12 call sites are unchanged.Verification
@mentra/sdk@3.0.0-hono.8): valid accepted; forged userId, tampered hash, wrong-API-key, and empty/malformed all rejected.generateFrontendTokenmy verifier uses, and@mentra/reactalways setsfrontendTokenalongsideuserId— so no regression in when the app connects.Operational note (pre-existing, not in this PR)
COOKIE_SECRETfalls back toMENTRAOS_API_KEYif unset (index.ts). Worth setting a distinctCOOKIE_SECRETin prod so session-cookie signing isn't tied to the API key.🤖 Generated with Claude Code
Note
High Risk
Security-critical auth changes on the primary sync channel and search APIs; incorrect deployment or token handling could break connections or leave residual bypasses.
Overview
Closes IDOR paths where callers could target another user’s notes and live transcripts by supplying a
userId.Backend:
/ws/syncno longer upgrades on?userId=; it requires a MentraOS frontend token (aos_frontend_tokenorBearer), verifies it withgenerateFrontendToken+ constant-time compare, and derivesuserIdfrom that. Search routes (/search,/search/backfill-status,/search/sentences) drop theuserIdquery fallback and userequireAuth(c)only. Dev transcript simulation endpoints return 404 outsideNODE_ENV=development.Frontend:
useSyncedconnects withfrontendTokenfromuseMentraAuth()and waits for the token before opening the socket. Search sendsAuthorization: Bearerinstead ofuserIdquery params. UnuseduseSSE(insecureuserIdon/api/events) is removed.Reviewed by Cursor Bugbot for commit 9c5fb3e. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by cubic
Fixes an IDOR by deriving user identity only from a verified MentraOS frontend token across WebSocket sync and search. Prevents cross-user reads/writes of private notes and transcripts.
Bug Fixes
/ws/syncnow requires a verified token via?aos_frontend_token=orAuthorization: Bearer <frontendToken>; userId is derived from the token; 401 otherwise. Verification usesgenerateFrontendTokenfrom@mentra/sdkwith a constant-time compare./api/search,/search/sentences, and/search/backfill-statusno longer accept?userId=; identity comes only fromrequireAuth(c)./api/test/simulate-*) return 404 unlessNODE_ENV=development.useSSEhook that called an unimplemented/api/events?userId=route.useSyncedauthenticates WS withfrontendTokenfrom@mentra/react;SearchPagesendsAuthorization: Bearer <frontendToken>.Migration
Authorization: Bearer <frontendToken>and WS with?aos_frontend_token=or the same header.useSyncedcall sites; it readsfrontendTokeninternally.Written for commit 9c5fb3e. Summary will update on new commits.