completeAuthorization reused an existing kc_login session's IssuedAt as authTime whenever one existed for the same user, even at the two call sites that had just validated an MFA token in this exact request. A platform-root sign-in that stepped up with fresh MFA while an older browser session cookie was still around got that older timestamp stamped onto the new authentication, understating freshness for every downstream binding-grade check (flex-auth's 900s review window denied informed-decision's infd-20260927-b01 approval as a result). Add a freshMFA parameter so only the carried-forward-assurance call site (no MFA challenge in this request) reuses the prior session's IssuedAt; the two call sites following a successful MFA.ValidateMFAToken always stamp authTime to now. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 169987@bnt-lap001 Assistant-Session: 322ef1ef-9048-4021-8570-b6d6f6347999
3 KiB
| id | type | title | domain | repo | status | owner | created | updated |
|---|---|---|---|---|---|---|---|---|
| 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 |
Stop completeAuthorization from reusing a stale prior session's IssuedAt after real MFA
id: ADHOC-2026-09-27-T01
status: done
priority: high
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.