[bug][collab] collab-token throttled by the anonymous public-share-AI limiter (5/min) → collaboration sync breaks after a few page opens #122

Closed
opened 2026-06-22 06:27:44 +03:00 by Ghost · 0 comments

Severity: high

POST /api/auth/collab-token is rejected with 429 after only 5 requests per 60s, because the route skips only the AUTH throttler and is still subject to the anonymous public-share-ai limiter (limit 5/60s).

Where:

  • apps/server/src/core/auth/auth.controller.ts:184collabToken() is decorated only with @SkipThrottle({ [AUTH_THROTTLER]: true }) (and the class skips AI_CHAT). It does not skip PUBLIC_SHARE_AI_THROTTLER.
  • apps/server/src/integrations/throttle/throttle.module.ts:32{ name: PUBLIC_SHARE_AI_THROTTLER, ttl: 60_000, limit: 5 } (and PAGE_TEMPLATE 30/60s) therefore also apply to collab-token. Effective cap = 5/min.

Repro (live): open ~8–10 page editors within a minute as a normal logged-in user. From the 6th open onward:

collab-token #1..#5  200
collab-token #9      429   retry-after-public-share-ai: 60
collab-token #10     429   retry-after-public-share-ai: 57

The 429 carries the header retry-after-public-share-ai, proving the anonymous public-share-AI bucket is the one rejecting it.

Impact: a single real user who opens more than 5 pages per minute starves their own collab token. The body editor stays locally editable (edits go to Yjs/IndexedDB) so there is no visible error, but the collab WebSocket can no longer authenticate → edits don't sync to the server / other clients, and it triggers the uncaught jwtDecode(undefined) crash (separate issue).

Suggested fix: also @SkipThrottle the PUBLIC_SHARE_AI (and PAGE_TEMPLATE) throttlers on collab-token, or move it behind the per-user AUTH throttler only.


Filed from an automated full-product QA pass on develop @ v0.93.0-64-gb60190ff, fresh DB. Each item below was reproduced live in a clean browser session unless noted.

**Severity:** high `POST /api/auth/collab-token` is rejected with **429** after only **5 requests per 60s**, because the route skips only the AUTH throttler and is still subject to the anonymous `public-share-ai` limiter (limit 5/60s). **Where:** - `apps/server/src/core/auth/auth.controller.ts:184` — `collabToken()` is decorated only with `@SkipThrottle({ [AUTH_THROTTLER]: true })` (and the class skips `AI_CHAT`). It does **not** skip `PUBLIC_SHARE_AI_THROTTLER`. - `apps/server/src/integrations/throttle/throttle.module.ts:32` — `{ name: PUBLIC_SHARE_AI_THROTTLER, ttl: 60_000, limit: 5 }` (and `PAGE_TEMPLATE` 30/60s) therefore also apply to collab-token. Effective cap = 5/min. **Repro (live):** open ~8–10 page editors within a minute as a normal logged-in user. From the 6th open onward: ``` collab-token #1..#5 200 collab-token #9 429 retry-after-public-share-ai: 60 collab-token #10 429 retry-after-public-share-ai: 57 ``` The 429 carries the header `retry-after-public-share-ai`, proving the anonymous public-share-AI bucket is the one rejecting it. **Impact:** a single real user who opens more than 5 pages per minute starves their own collab token. The body editor stays *locally* editable (edits go to Yjs/IndexedDB) so there is **no visible error**, but the collab WebSocket can no longer authenticate → edits don't sync to the server / other clients, and it triggers the uncaught `jwtDecode(undefined)` crash (separate issue). **Suggested fix:** also `@SkipThrottle` the `PUBLIC_SHARE_AI` (and `PAGE_TEMPLATE`) throttlers on `collab-token`, or move it behind the per-user AUTH throttler only. --- _Filed from an automated full-product QA pass on `develop` @ `v0.93.0-64-gb60190ff`, fresh DB. Each item below was reproduced live in a clean browser session unless noted._
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: vvzvlad/gitmost#122