Skip to content

feat(proxy): forward the browser user agent and pass the sign-in metrics route through - #161

Merged
Bccorb merged 1 commit into
mainfrom
feat/forward-client-user-agent
Sep 12, 2026
Merged

Bccorb merged 1 commit into
mainfrom
feat/forward-client-user-agent

Conversation

@Bccorb

@Bccorb Bccorb commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Adapter half of fells-code/seamless-auth-api#306 (the auth path instrumentation for fells-code/seamless-auth-api#154). Merge order: the API first, then this.

What changed

The browser's user agent is forwarded. The auth API now folds the request user agent into a device class on every audit row and reports sign-in outcomes by it. The adapter is the only client it sees, so until now every row carried this adapter's own user agent (node) and the breakdown by device class read unknown for every sign-in. Both adapters now send the browser's User-Agent as x-seamless-client-user-agent beside x-seamless-client-ip on every proxied call. The API honours it under the same service-token rule as the address.

It is trimmed and capped at 512 characters, which is what the API records. Unlike the IP it needs no trust decision: it is self-reported by the browser either way, and the API only uses it for telemetry, the session record and the magic link device binding. buildForwardedUserAgent tolerates a request with no headers, since getSeamlessUser accepts a caller-built one (the existing tests for it pass such a request).

The plumbing mirrors forwardedClientIp exactly: authFetch takes forwardedUserAgent, every core handler option type and passthrough carries it, and every adapter call site derives it next to the IP. That is 109 one-line additions across the three packages; there is no shorter route without a catch-all, which this repo deliberately does not have.

GET /internal/metrics/sign-ins is passed through, on an access session the way /internal/metrics/funnel is, with the from and to window forwarded. Core exports getSignInMetricsHandler. All seven passthrough files from the funnel change are touched the same way.

Verification

  • pnpm build clean for all three packages
  • pnpm test: core 247, express 172, fastify 69, all passing
  • New tests: authFetch sets the header only when given; express forwards it on an admin route and on /login (where no session exists yet) and caps an oversized one at 512; the fastify parity suite asserts both adapters forward the same value to upstream. The existing "sends no identity upstream for the public system config" parity case now pins one explicit user agent for both runners, because light-my-request sends a default and supertest sends none, and that difference showed up as a header one adapter forwarded and the other did not.
  • minor changeset for all three packages; READMEs describe the header

Downstream

The admin dashboard section that reads /internal/metrics/sign-ins is a separate issue in seamless-auth-admin-dashboard, linked from the API PR.

…ics route through

The auth API now records a device class on every audit row, folded from the
request user agent, and reports sign-in outcomes by it. The adapter is the only
client it sees, so every row carried the adapter's own user agent and the
breakdown read "unknown" for every sign-in. Both adapters now send the browser's
User-Agent as x-seamless-client-user-agent beside x-seamless-client-ip on every
proxied call, trimmed and capped at 512 characters. The API honours it under the
same service-token rule as the address.

GET /internal/metrics/sign-ins is proxied on an access session the way the
funnel route is, with the window forwarded and pinned by the query forwarding
tables and the parity cases.

Depends on fells-code/seamless-auth-api#306.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant