fix(security): RQ-3841 read the auth token from the URL fragment - #114
Open
rohitneharabrowserstack wants to merge 1 commit into
Open
fix(security): RQ-3841 read the auth token from the URL fragment#114rohitneharabrowserstack wants to merge 1 commit into
rohitneharabrowserstack wants to merge 1 commit into
Conversation
Prepares the client for requestly-cloud moving the Firebase custom token out of the query string. Ship and deploy this BEFORE the server change: the server half switches the token to the fragment, and a client that only reads window.location.search would break every OAuth login. - accessToken is now read from the fragment first, falling back to the query string. The fallback keeps this deploy-order-independent and covers cached or in-flight callback URLs; it can be dropped once the server change is live everywhere. - clearAccessTokenFromUrl() rewrites the address bar via replaceState once the token is captured, so the credential is not left visible to be copied, bookmarked, or read by a browser extension. It strips the whole fragment plus a legacy query accessToken, preserving every other param the flow reads (isNewUser, redirectURL). - The token is captured in a useMemo on mount, so clearing the URL afterwards cannot null it out mid-flow. Why the fragment: browsers never send it to a server, so the token stays out of Referer headers (this page loads Amplitude / Sentry / GrowthBook), out of GCP and CDN access logs, and out of anything else that records request URLs. Verified: file parses clean (tsc transpile, 0 syntax diagnostics); fragment round-trip checked with a token containing '+', '/' and '=' -- URLSearchParams percent-encodes on write and decodes on read, which naive concatenation would corrupt. getDesktopAppAuthParams reads localStorage, not the URL, so the desktop auth flow is unaffected by clearing the address bar. Server half: requestly-cloud RQ-3841. Refs RQ-3841 Co-Authored-By: Claude Opus 5 (1M context) <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.
Client half of RQ-3841. Merge and deploy this before the requestly-cloud half.
Why
/oauth/callbackcurrently redirects toapp.requestly.io/?accessToken=<firebase custom token>. A token in the query string is recorded in:Refererheader sent to every third-party script this page loads (Amplitude, Sentry, GrowthBook)The token exchanges for a Firebase refresh token, so one leaked log line becomes durable account access. Moving it to the fragment fixes this: browsers never send a fragment to a server.
Ordering — this must land first
The server change switches the token to the fragment. A client that only reads
window.location.searchwould then find no token and every OAuth login would break.This PR makes the client accept either location, so the two repos can deploy in any order:
The query-string fallback also covers cached / in-flight callback URLs. It can be deleted once the server change is live in beta and production.
Changes
useMemoon mount, so clearing the URL later cannot null it out mid-flow.clearAccessTokenFromUrl()— once the token is captured, rewrite the address bar withhistory.replaceState(no new history entry) so the credential is not left visible to be copied, bookmarked, or read by an extension. Strips the whole fragment plus any legacy queryaccessToken, while preserving every other param the flow reads (isNewUser,redirectURL).Verification
ts.transpileModule, 0 syntax diagnostics. I could not run a fulltsc/lint: I worked from a sparse shallow clone without the app's dependencies installed, so please let CI be the real check here.+,/and=.URLSearchParamspercent-encodes on write and decodes on read; naive string concatenation would corrupt such a token.getDesktopAppAuthParams()readslocalStorage, not the URL, so clearing the address bar cannot disturbauth_mode/ot-auth-code.Reviewer notes
useEffectdependency array gainedaccessToken. Behaviour is unchanged in practice (paramsis[]-memoized, soaccessTokenis stable for the component's life), but worth a look.?accessTokenfrom this redirect. I found only this component plusAuthPage.js/FirebaseActions.js/AuthHandler.tsreferencingsignInWithCustomToken, and none of the others parse the URL — but I was searching a sparse checkout.🤖 Generated with Claude Code