Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a14bfc-9643-7872-aa12-98326dcf25e7
5 KiB
| id | type | title | domain | repo | status | owner | created | updated | state_hub_workstream_id |
|---|---|---|---|---|---|---|---|---|---|
| ADHOC-2026-09-27 | workplan | Fresh MFA no longer backdated to a stale kc_login cookie (INFD-IN-0005) | infotech | key-cape | finished | claude | 2026-09-27 | 2026-09-27 | 5217dd06-73cb-50f8-ba64-528e421b29de |
Stop completeAuthorization from reusing a stale prior session's IssuedAt after real MFA
id: ADHOC-2026-09-27-T01
status: done
priority: high
state_hub_task_id: "ce98e4af-cfaa-5620-8f21-15ade18ee9e8"
informed-decision intake INFD-IN-0005 (open, origin_ref: SECRETS-WP-0010):
platform-root's approval of infd-20260927-b01 was refused by flex-auth's
compact_sitting policy with policy_denied, despite platform-root having just
completed a fresh key-cape MFA sign-in. flex-auth's 900-second review-freshness
check reads assurance.at, which key-cape derives from the PKCE session's
AuthTime.
Root cause in src/internal/server/oidc/authorize.go,
AuthorizeHandler.completeAuthorization: whenever a kc_login cookie already
existed for the same username, its IssuedAt was unconditionally reused as
authTime — even on the two call sites that had just called
MFA.ValidateMFAToken successfully in this exact request (the callback's
mfaToken != "" branch and serveMFASubmission). A user who had an older
browser session and then stepped up with a fresh MFA challenge got the old
session's timestamp stamped onto the new, just-proven authentication —
understating freshness for every downstream binding-grade check, exactly as
INFD-IN-0005 describes ("completeAuthorization currently copies a prior
timestamp... only after actual reauthentication").
The reuse itself is correct when MFA was not just performed in this
request (decision.MFAVerified carried forward from an existing AAL2 session,
via domain.DecideAssurance) — reporting mint time there would overstate
freshness (TestAssuranceReportsAuthenticationTimeNotMintTime). The two cases
were conflated under a single mfaVerified bool.
Fix: added a freshMFA bool parameter to completeAuthorization, true only
at the two call sites that just validated an MFA token in this request, false
at the carried-forward-assurance call site. authTime now falls back to the
prior session's IssuedAt only when !freshMFA.
Added TestAuthorizeCallback_MFASubmission_FreshEvenWithStalePriorLoginCookie,
which seeds a 4-hour-old kc_login cookie for the same user, then submits a
valid MFA token and asserts the resulting AuthTime is within a minute of now.
Confirmed it fails against the pre-fix code (AuthTime ~4h old) and passes
after. Full suite (go test ./...) green, no other test needed adjustment —
the carried-forward-assurance case (TestPolicy_..., TestLogout_...,
assurance_time_test.go) is unaffected since none of it exercises a fresh MFA
submission with a pre-existing cookie for the same user.
Does not touch flex-auth (its 900s window is correct; it was consuming a bad
timestamp) or informed-decision (correctly refuses to interpret the PDP's
denial). INFD-IN-0005 can close once informed-decision confirms platform-root
can approve after a fresh sign-in against this build.
Discovery/JWKS fetch was missing the split-horizon origin headers
id: ADHOC-2026-09-27-T02
status: done
priority: high
state_hub_task_id: "7fa0bfc7-0772-57c7-8d6f-641615cab82a"
Rolling out T01 (image digest sha256:248e449a...) broke every KeyCape login,
including unrelated clients (user-engine-portal), with auth_failure /
provider_metadata_unavailable at /api/oidc/token on the KeyCape side and
Authelia logging invalid X-Forwarded-Proto header value 'http' on
GET /.well-known/openid-configuration from KeyCape's pod IP.
Root cause, unrelated to T01's change: AutheliaAdapter.setPublicOriginHeaders
(the NK-WP-0041-T02/path-B fix, key-cape@3b0446e) annotates only the token
exchange (newTokenRequest) with X-Forwarded-Proto/X-Forwarded-Host so
Authelia can derive the public issuer for a split-horizon (in-cluster HTTP,
public HTTPS) deployment. idTokenVerifier.get in verify.go — used for the
provider-metadata and JWKS fetches that verify every upstream ID token — never
got the same treatment; it built a bare request with only an Accept header.
Authelia 4.38 fails closed on the resulting unmarked in-cluster request. This
gap predates today's session (present since 3b0446e); it surfaced now because
this was the first login attempted since that image went live.
Fix: extracted setPublicOriginHeaders from an AutheliaAdapter method to a
package-level function taking the target public base URL explicitly, and gave
idTokenVerifier its own resolved publicBaseURL (same
BrowserBaseURL-else-BaseURL fallback as the adapter) so get() applies
the same headers. Added TestDiscoveryFetch_CarriesPublicOriginHeaders
(confirmed it fails without the fix, passes with it). Full suite green.
New image key-cape@<pending> still needs a rollout to sso-mfa/k8s/keycape/deployment.yaml
before platform-root can sign in again.