fix(sessions): show external IP and a detected device on Active Sessions (BUNYIP-409) #404
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/BUNYIP-409-session-ip-device"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Symptoms
The Active Sessions list showed bunyip-api's Docker-internal peer IP and "Unknown device" for every session.
Trace (before changing code)
Active Sessions reads refresh_tokens, created in bunyip-api's login/register/2FA/approval handlers from
extract_client_ip(req)+extract_device_info(req). But that request is bunyip-web -> bunyip-api server-to-server, so both the peer IP and the User-Agent are bunyip-web's, not the browser's.extract_client_ipis already the correct forwarded-header-aware extractor (same oneaccess_log.rs/root_span.rsuse, with anti-spoof tests). It honours a forwardedX-Forwarded-Foronly when the peer is insideTRUSTED_PROXY_CIDR- and both that (api) andWEB_TRUSTED_PROXY_CIDR(web) defaulted to empty. With no trusted proxy configured, the BFF forwards nothing and the API falls back to the Docker peer.Fix
IP (config, no extractor change): default both
TRUSTED_PROXY_CIDR(api) andWEB_TRUSTED_PROXY_CIDR(web) to the RFC1918 private ranges incompose.ymlandcompose.dev.yml. The API is only reachable over the internal Docker network + Traefik, so the only peers are trusted infra;.env.examplealready recommended exactly these ranges. The extractor and its anti-spoofing tests are unchanged - a forged header from an untrusted peer is still ignored.Device (code): bunyip-web captures the inbound browser
User-Agentinto a task-local (mirroring the existing client-IP forwarding) and sets it on every outbound/v1call, soextract_device_inforecords the real browser UA. Newbunyip_domain::device::device_labelmaps a UA to "Chrome on macOS" / "Safari on iOS" etc. (browser + OS via substring detection, no new dependency), applied at read time inlist_sessionsso existing rows benefit with no backfill. An absent/unrecognisable UA ->None-> the web keeps its "Unknown device" fallback.Acceptance criteria
resolve_client_ip/resolve_forwarded_iptests; extractor unchanged).Verification
just check-containergreen: fmt + clippy-D warnings+ all test binaries (121 web tests). New tests: the device-label parser (Edge-vs-Chrome and iOS-vs-macOS token ordering;Nonefor unknown/absent UAs) and that the BFF middleware exposes the browser UA to the outbound-call path.Note: I could not do the observed production-ingress run locally - the dev stack is currently blocked by a pre-existing Postgres major-version mismatch on the persisted dev volume (unrelated to this change; I left the volume untouched).
🤖 Generated with Claude Code