Uh oh!
There was an error while loading. Please reload this page.
fix(auth): auto-refresh on PostHog 401/403 auth failures - #2186
Conversation
PostHog's OAuth access tokens can go dead before the client's local
`accessTokenExpiresAt` expires (the previous access token is invalidated
when a new one is minted via refresh). When that happens, callers see
HTTP 401/403 with bodies the proxy and renderer fetcher weren't
matching, so neither auto-recovered.
- MCP proxy: retry-with-refresh on HTTP 401 in addition to the existing
JSON-RPC body sentinels. Extend the sentinel set to match the literal
strings the servers actually return (`Invalid API key` from
mcp.posthog.com via Cloudflare, `Authentication failed` from the
us.posthog.com installation proxy).
- Renderer fetcher: retry-with-refresh on HTTP 403 when the body is
`{type: "authentication_error", code: "authentication_failed"}` (the
shape PostHog's Django API returns for invalid bearer tokens), in
addition to the existing 401 retry.
Generated-By: PostHog Code
Task-Id: 83ede0c1-3f83-4fe3-a4bd-69cf379be315
|
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Drop bare HTTP 401 trigger from MCP proxy retry — authenticatedFetch already retries on 401/403, so the second refresh was redundant and risked churning tokens under the server's in-place access-token rotation behavior. - Gate the new substring matches (`Invalid API key`, `Authentication failed`) on `status >= 400` to avoid spurious refreshes when a 200 JSON-RPC tool result legitimately mentions those phrases. - Add tests covering the renderer fetcher's 403-with-authentication_failed retry path and adjacent negative cases. Generated-By: PostHog Code Task-Id: 83ede0c1-3f83-4fe3-a4bd-69cf379be315
Uh oh!
There was an error while loading. Please reload this page.
Mirror the desktop fix from #2186 on mobile. PostHog API requests now transparently refresh the OAuth token and retry once on a 401 — or a 403 whose body is shaped like an authentication error (`code: "authentication_failed"` / `type: "authentication_error"`). If the refresh itself fails, the original response is returned so callers stay on their existing error/sign-out paths. Introduces `authedFetch(url, init)` in `apps/mobile/src/lib/api.ts` and threads it through every PostHog request site (`tasks`, `inbox`, `mcp`, `skills`, push tokens, user/projects queries). A module-level in-flight promise dedups concurrent 401s so a stampede only triggers a single refresh. The SSE stream endpoint (`streamCloudTask`) keeps its raw `fetch` — it has its own resumption flow via `Last-Event-ID` and can't be replayed with a fresh token. Adds `apps/mobile/src/lib/api.test.ts` mirroring the desktop fetcher tests, plus dedup coverage for the stampede case. Generated-By: PostHog Code Task-Id: bece83a6-7305-4a9d-b7c3-c5cb62ee3e49
MCP tool discovery and renderer task creation were surfacing 401/403
authentication_failedto users because the existing refresh-and-retry paths didn't match the real error shapes (Invalid API key,Authentication failed, 403 withauthentication_errorbody).Extend the MCP proxy to retry-with-refresh on HTTP 401 plus the literal server strings, and the renderer fetcher to retry on 403 with
authentication_failedin addition to 401.Created with PostHog Code