Each request to a gated endpoint now triggers a fresh FIDO2 assertion.
Challenge = SHA256(url|method|nonce) — bound to the specific resource.
The self-contained assertion bundle lets the server verify independently
without calling back to the phone.
- fido2_ops.dart: GetAssertionResult gains clientDataJson; getAssertion
accepts optional challenge override
- proxy_service.dart: _handleAuthGetToken accepts {url,method,nonce},
derives challenge, runs card assertion, returns b64url bundle
- filter_proxy.dart: _getAuthToken(uri, method) generates nonce and
passes binding fields to Component 2
- component3/phone.go: stateless GetTokenForRequest(url, method) —
no session caching, no expiry, one card touch per request
- component3/proxy.go: use GetTokenForRequest
- component3/main.go: remove --user flag (Component 2 picks enrolled user)
- k_server_app.py: _verify_assertion_token() — verifies path+method
match, challenge claim, and ECDSA-P256 signature; accepts both
legacy X-Proxy-Token and new Bearer assertion tokens
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Local branch was started fresh on new Mac (2026-04-29) as a workspace
snapshot of the full project state, then extended with Phase 9 k_phone
work. Remote has the complete git history up to 2026-04-27.
This merge grafts the two histories together. Local content is preserved
in full (it is a strict superset of the remote state).
proxy_service.dart: _handleConnect gates on hasAnyActiveSession() (407 if
no active session), then connects directly to the upstream external target
(host:port from Host header), detaches the socket, and pipes bytes
bidirectionally. k_server is not involved in CONNECT tunnels.
filter_proxy_test.dart: replace _mockTcp() with _mockComp2Tcp() in the
CONNECT routing group so the mock speaks the full CONNECT handshake
(reads request headers, sends 200 Connection Established, pauses sub).
All 21 tests pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- k_phone/lib/filter_proxy.dart: Component 1 — raw-socket HTTP proxy with
gating filter; gated hosts relay to Component 2, others go direct
- k_phone/lib/session_manager.dart: add hasAnyActiveSession() for the
personal-device gated-proxy authorization model
- k_phone/test/filter_proxy_test.dart: full test suite for Component 1
- k_phone/test/enrollment_test.dart: full test suite for EnrollmentDb
- k_phone/integration_test/registration_login_test.dart: emulator integration test
- Misc k_phone lib fixes (ctaphid_channel, fido2_ops, proxy_service, main,
enrollment_db, k_server_client) and pubspec/Gradle updates
- CLAUDE.md + Workplan.md: document Component 1, k_phone module map,
gated terminology (replacing "allowlist"), pending CONNECT handler in
Component 2
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Records CardEmulator addition, the two fido2-direct id=/raw_id= bug fixes,
and the expanded test count (100 → 122). Marks project status unchanged:
Phases 7 and 9 remain externally gated.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
tests/card_emulator.py: software emulator of the ChromeCard FIDO2
authenticator. Implements make_credential and get_assertion with real P-256
cryptography and an in-memory credential store. Both methods accept
user_confirms=True/False to simulate the card's Yes/No confirmation dialog;
False raises CtapError(OPERATION_DENIED). refusing() returns a wrapper that
forces user_confirms=False for integration tests that route through
_with_direct_ctap2. forget_user() simulates card-side credential removal.
Module docstring serves as the usage guide.
tests/test_k_proxy.py: 22 new tests across TestCardEmulatorUnit (direct
emulator calls) and TestCardEmulatorIntegration (full ProxyState flows
covering register, authenticate, user-says-no, forget, two-user isolation,
and sign-count monotonicity).
k_proxy_app.py: fix two bugs where RegistrationResponse and
AuthenticationResponse were constructed with id= instead of raw_id=. Both
calls raised TypeError at runtime, silently caught by the surrounding except
block, making all fido2-direct register and authenticate calls fail.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- fido2-direct confirmed as deployed default after full browser flow with real card
- Document enroll-clears-session bug fix
- Document k_proxy unit test suite (100 tests)
- Record current deployed service state and port map
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Update module docstrings to concise service descriptions
- Add _require_json() helper to Handler in k_proxy and k_client_portal,
eliminating repetitive try/except JSON-parse blocks in handler methods
- Cache SSL context once in ClientState.__init__ instead of per-request
- Fix: ClientState.enroll() now calls /session/logout on k_proxy before
re-enrolling, so the old server-side session is invalidated rather than
left to expire (discovered via live test where re-register after login
caused subsequent logout to fail with missing bearer token)
- Add targeted comments explaining non-obvious invariants: _gc_locked lock
ownership, _with_direct_ctap2 retry-on-reopen, _require_session None
convention, will_close connection reuse, HTTP/1.1 body-drain requirement,
90 s interactive timeout margin, and enroll session-clearing rationale
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
100 tests covering session management, enrollment CRUD, probe and direct
FIDO2 auth routing, UpstreamPool connection handling, and all HTTP
endpoints via a live in-process server. Card (FIDO2/CTAP) and k_server
are fully mocked so the suite runs locally without hardware or VMs.
Also hardens the fido2.features.webauthn_json_mapping import guard to
tolerate older python-fido2 versions that lack the attribute.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>