The remaining P3 tail is done:
- §13.5: three TOTP codes for the shared RFC secret computed by an
independent implementation (Python stdlib, anchored to RFC 4226 App D
counter 0 = 755224) at recorded wall-clock Unix seconds, pinned in
platform/auth TestInteropCapturePythonTOTP — the Go code must produce
identical codes at the same instants and VerifyWindow(0) must accept
each at its capture time (cross-implementation agreement).
- §14.4: a full WebAuthn ceremony through real Chrome (navigator.credentials
with a CDP virtual authenticator: ctap2/internal, resident key, UV,
ES256) against a live go-auth instance — registration + passwordless
login both completed via the RP handlers. The raw browser-produced
blobs (clientDataJSON, attestationObject, authenticatorData, signature)
are stored verbatim as a golden fixture and replayed by
TestInteropCaptureChromeRegistration/Assertion: the parser must decode
the real CBOR/COSE/authenticator-data, and the real ECDSA signature
must verify over SHA-256(authData ‖ clientDataJSON) with the parsed key.
Plan doc §13.5/§14.4 updated with the recorded values; P3 row marked
complete. Both new suites green.
🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
The authenticator-app second factor (D4, plan §5.7): shared RFC 4226/6238
primitive in platform/auth/totp.go (±1 window, monotonic replay floor, §13
RFC vectors), per-user AES-GCM-wrapped secrets under AUTH_TOTP_WRAP_KEY,
hashed one-shot recovery codes, and the password → /totp challenge (or
recovery) → session browser flow with first-login enrollment (otpauth QR via
vendored qrcode-generator, two-code confirm, once-only recovery reveal).
AUTH_REQUIRE_TOTP defaults on; per-challenge throttling (R5). Covered E2E by
internal/oidc/totp_flow_test.go.
Passkeys (D6, plan §5.8): go-auth is its own WebAuthn RP — hand-rolled
CBOR/COSE subset parser (stdlib only), register/login ceremonies with
attestation:none, per-request RP ID + origin derived from the issuer host,
AOF-backed cred:<user>:<credID> store. The /admin UsersManager (invite,
disable, reset password, list/rename/delete passkeys, enroll in-browser) is
session + admin-role gated. WebAuthn vectors + ceremony negatives in
internal/webauthn + webauthn_flow_test.go.
Also: bare-host GET / now redirects to /login (the issuer root otherwise
404s in a browser).
Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
The key store holds only SHA-256 hashes + metadata (no plaintext keys) and is
read by other services in their own containers running as different non-root
uids. saveLocked wrote it 0600, so when the backoffice pod (running as root)
minted or revoked a key the file became root-owned 0600 — unreadable to the
ledger/cart services, freezing their mtime-reload snapshots: new keys 401'd
and revocations never took effect. Write 0644 so every reader reloads.
🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
The approve UI needs to show what a user code asks for and how long it
is valid; GrantInfo returns (scopes, secondsLeft, ok) without consuming
the grant (consumption stays one-shot in Token).
platform/auth gains the LocalJWT (HS256 mint/verify, AUTH_TOKEN_SECRET,
disabled when unset), the RFC 8628 DeviceFlow (start/approve/poll with
requested-scope gating) and KeyStore.ReloadIfChanged (mtime-based, so a
freshly minted key works across processes without a restart). One shared
store at /commerce/apikeys.json (AUTH_APIKEYS_FILE) now backs the
backoffice admin gate, the CMS API and every MCP endpoint; AUTH_TOKEN_SECRET
lets the companion app's device flow mint bearer tokens the same surfaces
accept. Submodule bumps: backoffice (AuthGate + device endpoints + mcp
scope), go-cart-actor (mcpauth sources), go-graph-cms-v2 (shared store +
token chain).
Also lands the still-pending receipt-ocr sidecar (previous pass): the
PP-OCR ONNX inference service + download-ocr-models.sh + k8s
deployment/initContainer, wired into compose and the ledger RECEIPT_OCR_URL,
with the extractor pairing fix riding the go-cart-actor bump.
Deploy: AUTH_APIKEYS_FILE/AUTH_TOKEN_SECRET/AUTH_DEVICE_BASE_URL on
backoffice + cart/profile/order/ledger in compose and k8s, /device ingress
route, stack-secrets AUTH_TOKEN_SECRET optional ref, docs updated.
Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>