Repository navigation
fix(auth): deprecate and retire the ?token= query parameter #468
Copy link
Copy link
Open
Labels
area/apiHTTP handlers, routing, middlewareHTTP handlers, routing, middlewarearea/authAuthentication: tokens, JWT/JWKS, keys, token expiry/revocationAuthentication: tokens, JWT/JWKS, keys, token expiry/revocationbreaking-changeBreaking change to public API, CLI, or configBreaking change to public API, CLI, or configenhancementNew feature or requestNew feature or requestsecuritySecurity-sensitive issue or fixSecurity-sensitive issue or fix
Description
Activity
- addedenhancementNew feature or requestNew feature or requestarea/apiHTTP handlers, routing, middlewareHTTP handlers, routing, middlewaresecuritySecurity-sensitive issue or fixSecurity-sensitive issue or fixbreaking-changeBreaking change to public API, CLI, or configBreaking change to public API, CLI, or configarea/authAuthentication: tokens, JWT/JWKS, keys, token expiry/revocationAuthentication: tokens, JWT/JWKS, keys, token expiry/revocation
on Aug 13, 2026 coderabbitai commented
on Aug 13, 2026 coderabbitaiboton Aug 13, 2026 – with coderabbitaiMore actions🔗 Related PRs
#123 - fix(api): drop CORS credentials + skip same-origin decoration [merged]
#174 - refactor(table names): handle unsafe table names [closed]
#378 - feat(auth): non-JWT operator key for admin + break-glass access [merged]
#448 - fix(sdk): keep a baseURL path prefix instead of discarding it [merged]
🧪 Issue enrichment is currently in open beta.
You can configure auto-planning by selecting labels in the issue_enrichment configuration.
To disable automatic issue enrichment, add the following to your
.coderabbit.yaml:issue_enrichment: auto_enrich: enabled: false
💬 Have feedback or questions? Drop into our discord!
- added a commit that references this issue
on Aug 18, 2026 - added a commit that references this issue
on Oct 6, 2026
Metadata
Metadata
Assignees
Labels
area/apiHTTP handlers, routing, middlewareHTTP handlers, routing, middlewarearea/authAuthentication: tokens, JWT/JWKS, keys, token expiry/revocationAuthentication: tokens, JWT/JWKS, keys, token expiry/revocationbreaking-changeBreaking change to public API, CLI, or configBreaking change to public API, CLI, or configenhancementNew feature or requestNew feature or requestsecuritySecurity-sensitive issue or fixSecurity-sensitive issue or fix
Type
Projects
- StatusShow more project fieldsIn progress
Area: api / auth · security-hardening — the Go half of #203, which closes it.
#203 moved the TypeScript SDK's streaming transport onto
fetch, so it now authenticates withAuthorization: Bearerand never puts a credential in the URL. That was the first two of #203's three tasks. This is the third: retiring the?token=query parameter server-side.Splitting it out deliberately — #203's PR is SDK-only, and this half touches the auth middleware (security-labelled, heavier review) while blocking nothing.
Why it still matters after #203
The SDK no longer sends
?token=, but the server still accepts it (internal/auth/auth.go:268-282), so nothing has actually stopped: any consumer still using it — a hand-rolled browserEventSource, a shell script, another language's SSE client — keeps putting a JWT in the request URI, where every proxy, CDN, and load balancer in front of WaveHouse can log it.bearerTokenstrips?tokenfromr.URLafter extraction, which keeps it out of WaveHouse's own logs, but that is the last hop; by then it has already crossed every intermediary in the request line.The header has always been preferred when both are present, so the deprecation is about removing a fallback, not changing precedence.
Proposed staging
Phase 1 — signal and measure (this issue). Keep accepting it, make its use visible:
wavehouse_auth_query_token_total, following the existingwavehouse_auth_operator_key_failures_totalprecedent (internal/auth/auth.go:38-40) — an OTelInt64Counteron thewavehouse-authmeter. This is the load-bearing part: an operator cannot safely remove the parameter without knowing whether anything still uses it.Deprecation: true(RFC 9745) and aSunset:date (RFC 8594) naming the removal release./v1/streamis the main consumer of this path and connections are long-lived but numerous; a WARN per request is log spam that operators will filter out, defeating the point. A once-per-process log at startup-ish cadence, or nothing beyond the counter, is better. (Contrast the operator-key WARN, which is justified because a mismatch is a probing signal — ordinary?token=use is not.)Phase 2 — opt-in enforcement. An
auth.allow_query_tokenconfig knob (defaulttrueinitially) so an operator who has confirmed zero usage can fail closed immediately rather than waiting for a major.Phase 3 — removal. Flip the default to
false, then delete the fallback frombearerToken. Breaking; needs a major and a CHANGELOG entry.Phases 2 and 3 could be folded or dropped depending on what the counter shows — if usage is zero across deployments, the runway can be short. Worth noting the whole surface is pre-1.0 and the SDK is not yet on npm, so removal will never be cheaper than it is now; the argument for a runway is entirely about non-SDK consumers, whom the counter will identify.
Implementation notes
bearerToken(auth.go:271-276) happens before either return, so a header-authenticated request carrying a stray?tokenalso strips it. Count only the case where the query token is actually used — i.e. noAuthorization: Bearerwas present — or the metric will over-report.auth.go:249-250); nothing changes there./v1/streamflushes headers immediately (internal/api/stream.go).Docs to update when the behavior changes
docs/src/content/docs/api.md:20-28— the?token=section and the precedence paragraphdocs/src/content/docs/reverse-proxy.mdx:137— the forwarding checklistdocs/src/content/docs/access-control.mdx:43— the pointer to the query fallbackCHANGELOG.mdAcceptance
wavehouse_auth_query_token_totalincrements only when the query token is the credential actually usedDeprecation/Sunsetheaders on those responses, including on/v1/streambefore the stream body startsRelated: #203 (SDK half, done), #239 (enforce token expiry on long-lived SSE — also touches this middleware), #228 (auth-hardening epic).