Skip to content

fix: forward browser User-Agent + ADC Local Test Run 4 defects - #495

Merged
ivanball merged 3 commits into
mainfrom
fix/forward-browser-user-agent
Oct 4, 2026
Merged

ivanball merged 3 commits into
mainfrom
fix/forward-browser-user-agent

Conversation

@ivanball

@ivanball ivanball commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

Follow-up to #492 (v1.225.0), found while verifying it in production.

What production showed: after the ADC deploy, a sign-in through the Blazor Server path recorded the visitor's real public IP on the Signed-in devices page (so the #492 X-Forwarded-For forwarding works), but the device column read "Unrecognized device": the server-side "APIClient" call carried no user-agent of the browser, only the host's empty one.

Fix: the handler that AddCommonServerTokenStorage() composes onto "APIClient" now also stamps User-Agent with the visitor's user-agent, from the same request and with the same semantics CookieSessionRefresher.BrowserOrigin already uses for the refresh call. A blank user-agent is not forwarded; an existing value is replaced, never appended. The internal handler is renamed BrowserForwardedForHandler -> BrowserOriginHandler (internal, no public API change).

Tests: BrowserOriginHandlerTests keeps the six address tests and adds: user-agent forwarded, an existing user-agent replaced, a blank user-agent not sent (address still sent), and the DI wiring asserting both headers. MMCA.Common.UI.Web.Tests 208/208 locally; Release build 0 warnings.

Ships in the next Common release; consumers pick it up with no code change (ADC and Store already call AddCommonServerTokenStorage()).

Also in this PR: ADC Local Test Run 4 defects (found 2026-10-03/04 by a 231-case manual run on a fresh local ADC stack; each confirmed in source by an independent reviewer; the tests were written in a separate pass before the fixes):

  • Registration burst bypass (U-23, medium): LoginProtectionService read the per-IP registration count through the hybrid cache, whose 30 s in-process copy IncrementAsync never refreshes, so 11 registrations from one IP within a few seconds all succeeded. It now reads the shared store (GetFromSharedStoreAsync), as 2FA and password reset already do. Test: LoginProtectionServiceHybridCacheTests over the real hybrid cache.
  • Gateway /health flapping (low): the health-check factory built a new DownstreamServiceHealthCheck per poll, losing the latched HTTP version, and the probe clients carried the standard Polly retry inside the 2 s budget, so a mixed-profile downstream (Notification) flapped Unhealthy while every route worked. Now one keyed-singleton check per downstream, and no resilience handler on the probe clients (written against the stable ConfigureAdditionalHttpMessageHandlers; RemoveAllResilienceHandlers is still EXTEXP0001). Test: GatewayDownstreamHealthCheckPollingTests.
  • "Loading cancelled." on every organizer grid load (O-39, low): DataGridListPageBase toasted any OperationCanceledException, including a load superseded by a newer one. It now toasts only for the current load of a live component; a user Cancel still toasts. Tests: three new facts in DataGridListPageBaseTests (the existing O-72 test now waits on load completion instead of the toast; no assertion changed).
  • Spanish accents (X-01, low): role administration and the OAuth completion strings regain their accents, and the new public SpanishAccentTestsBase (Testing.Architecture, Governance) fails the build on an unaccented Spanish word in any .es.resx; Common subclasses it, and ADC will in its bump PR (ivanball/ADC#248).
  • Docs: INotificationScopeProvider no longer claims the targeting caption shows who a notification reaches; the scope tags the inbox view and delivery goes to every recipient.

Local: dotnet build MMCA.Common.slnx -c Release 0 warnings; Architecture 318/318, Aspire 293/293, UI 1232/1232, Infrastructure 1725/1725.

Release plan: this PR -> v1.226.0 -> one bump PR per consumer (ADC #248 carries its own fixes too, so one deploy; Store and Helpdesk bump PRs).

🤖 Generated with Claude Code

https://claude.ai/code/session_01Ke2CLcwmJVENRiYrRVmGKw

ivanball and others added 2 commits October 3, 2026 21:47
The server-side "APIClient" handler from AddCommonServerTokenStorage()
now stamps User-Agent with the visitor's user-agent next to
X-Forwarded-For (same source and semantics as the cookie-session
refresh), so a sign-in or registration on the Blazor Server path records
the browser's device on the signed-in devices page instead of
"Unrecognized device". Verified in production after v1.225.0: a
server-path sign-in recorded the public IP but no device name.

The internal handler is renamed BrowserForwardedForHandler ->
BrowserOriginHandler since it now forwards both values. A blank
user-agent is not forwarded; an existing value is replaced.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sDGvYdTD88ZjjD7if2SMW
…g, cancel toast, Spanish accents)

- LoginProtectionService reads the per-IP registration counter from the
  shared store (GetFromSharedStoreAsync): the hybrid cache served a 30 s
  in-process copy the increment never refreshed, so a burst of registrations
  from one IP was never refused (U-23).
- Gateway downstream health checks: one keyed-singleton check per downstream
  so the latched HTTP version survives between polls, and no resilience
  handler on the probe clients, whose retry backoff ate the 2 s probe budget;
  /health no longer flaps to 503 while every route works.
- DataGridListPageBase toasts "Loading cancelled." only for the current load
  of a live component: a load superseded by a newer one, or cancelled by
  disposal, is silent; a user Cancel still toasts (O-39).
- Spanish: role administration and the OAuth completion strings regain their
  accents; new SpanishAccentTestsBase fitness test (Testing.Architecture
  Governance) fails the build on an unaccented Spanish word in any .es.resx.
- INotificationScopeProvider docs: the scope tags the inbox view; delivery
  goes to every recipient.

Tests for each fix were written in a separate pass before the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ke2CLcwmJVENRiYrRVmGKw
@ivanball ivanball changed the title fix: forward the browser User-Agent on server-side API calls fix: forward browser User-Agent + ADC Local Test Run 4 defects Oct 4, 2026
@ivanball
ivanball merged commit fc36184 into main Oct 4, 2026
16 checks passed
@ivanball
ivanball deleted the fix/forward-browser-user-agent branch October 4, 2026 05:13
@ivanball ivanball mentioned this pull request Oct 4, 2026
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