From 205a1c0edc9401275ea2f855c159448fd29577b5 Mon Sep 17 00:00:00 2001 From: tegwick Date: Wed, 14 Oct 2026 18:29:33 +0200 Subject: [PATCH] Link the MFA challenge to lost-authenticator help. The OTP page now points at the account security page without carrying challenge state, and the render test checks that link. Workplan headings sit above their task blocks so the hub titles match KEY-WP-0038 and KEY-WP-0039. KEY-WP-0039-T02 stays waiting on the Forgejo identity gate. Assistant: grok Assistant-Session: 01a13b3b-6856-7900-9d04-bccba2337a30 --- .custodian-brief.md | 19 +++++++------- WORK-RECORDS.md | 8 +++--- src/internal/server/oidc/authorize.go | 1 + src/internal/server/oidc/authorize_test.go | 10 ++++++- workplans/KEY-WP-0038-forgejo-oidc-client.md | 18 ++++++------- ...Y-WP-0039-openbao-negative-probe-tokens.md | 26 +++++++++++++------ .../KEY-WP-0040-mfa-attended-run-findings.md | 6 ++++- 7 files changed, 55 insertions(+), 33 deletions(-) diff --git a/.custodian-brief.md b/.custodian-brief.md index 7de901c..f33d0b3 100644 --- a/.custodian-brief.md +++ b/.custodian-brief.md @@ -2,23 +2,24 @@ # Custodian Brief — key-cape **Domain:** infotech -**Last synced:** 2026-10-11 21:03 UTC +**Last synced:** 2026-10-12 05:31 UTC **State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)* ## Active Workstreams -### Fix findings from the first attended MFA run (factor recovery preview, OTP prompt help) -Progress: 0/2 done | workplan_id: `a67f9d60-9b8f-5111-9fe5-9f64e631b1c3` - -**Open tasks:** -- · Recovery preview rejects valid input (F6) `b385602d` -- · Link the OTP prompt to lost-authenticator help (F3) `3ad155dd` - ### Negative-check tokens for the secrets-engine OpenBao JWT role Progress: 2/3 done | workplan_id: `09c3bddf-6058-5603-8488-cccdb7be4f86` **Open tasks:** -- · T01 — Define the probe registrations `03da4c38` +- ! T01 — Define the probe registrations `03da4c38` + *(wait: Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; registration authorization already resolved.)* + +### Fix findings from the first attended MFA run (factor recovery preview, OTP prompt help) +Progress: 0/2 done | workplan_id: `a67f9d60-9b8f-5111-9fe5-9f64e631b1c3` + +**Open tasks:** +- ► Recovery preview rejects valid input (F6) `b385602d` +- ► Link the OTP prompt to lost-authenticator help (F3) `3ad155dd` ### Forgejo as a NetKingdom OIDC client, so the forge can close Progress: 2/4 done | workplan_id: `098e3e2c-a138-560e-b2d3-3a15eb49d8a9` diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index 2a7c960..c3c2092 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -50,7 +50,7 @@ | workplan | KEY-WP-0037 | finished | — | workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md | | workplan | KEY-WP-0038 | active | — | workplans/KEY-WP-0038-forgejo-oidc-client.md | | workplan | KEY-WP-0039 | active | — | workplans/KEY-WP-0039-openbao-negative-probe-tokens.md | -| workplan | KEY-WP-0040 | ready | — | workplans/KEY-WP-0040-mfa-attended-run-findings.md | +| workplan | KEY-WP-0040 | active | — | workplans/KEY-WP-0040-mfa-attended-run-findings.md | | task | ADHOC-2026-09-05-T01 | done | — | workplans/ADHOC-2026-09-05.md | | task | ADHOC-2026-09-07-T01 | done | — | workplans/ADHOC-2026-09-07.md | | task | ADHOC-2026-09-27-T01 | done | — | workplans/ADHOC-2026-09-27.md | @@ -214,8 +214,8 @@ | task | KEY-WP-0038-T03 | wait | — | workplans/KEY-WP-0038-forgejo-oidc-client.md | | task | KEY-WP-0038-T04 | cancel | — | workplans/KEY-WP-0038-forgejo-oidc-client.md | | task | KEY-WP-0039-T01 | done | — | workplans/KEY-WP-0039-openbao-negative-probe-tokens.md | -| task | KEY-WP-0039-T02 | todo | — | workplans/KEY-WP-0039-openbao-negative-probe-tokens.md | +| task | KEY-WP-0039-T02 | wait | — | workplans/KEY-WP-0039-openbao-negative-probe-tokens.md | | task | KEY-WP-0039-T03 | done | — | workplans/KEY-WP-0039-openbao-negative-probe-tokens.md | -| task | KEY-WP-0040-T01 | todo | — | workplans/KEY-WP-0040-mfa-attended-run-findings.md | -| task | KEY-WP-0040-T02 | todo | — | workplans/KEY-WP-0040-mfa-attended-run-findings.md | +| task | KEY-WP-0040-T01 | progress | — | workplans/KEY-WP-0040-mfa-attended-run-findings.md | +| task | KEY-WP-0040-T02 | progress | — | workplans/KEY-WP-0040-mfa-attended-run-findings.md | | intake | KEY-IN-0001 | closed | — | intakes/intakes.md | diff --git a/src/internal/server/oidc/authorize.go b/src/internal/server/oidc/authorize.go index 69e5112..7f976c5 100644 --- a/src/internal/server/oidc/authorize.go +++ b/src/internal/server/oidc/authorize.go @@ -760,6 +760,7 @@ var mfaChallengeTemplate = template.Must(template.New("mfa-challenge").Parse(` +

Code rejected or lost your authenticator?

`)) diff --git a/src/internal/server/oidc/authorize_test.go b/src/internal/server/oidc/authorize_test.go index ade18a6..434b510 100644 --- a/src/internal/server/oidc/authorize_test.go +++ b/src/internal/server/oidc/authorize_test.go @@ -531,11 +531,19 @@ func TestAuthorizeCallback_MFARequired_RendersChallengeWithoutToken(t *testing.T t.Errorf("expected 200 challenge page, got %d (body: %s)", w.Code, w.Body.String()) } body := w.Body.String() - for _, want := range []string{"Verify sign-in", "alice", "Test Client", `name="mfa_token"`} { + for _, want := range []string{ + "Verify sign-in", "alice", "Test Client", `name="mfa_token"`, + `href="https://users.coulomb.social/security"`, + `rel="noreferrer"`, + "Code rejected or lost your authenticator?", + } { if !strings.Contains(body, want) { t.Errorf("challenge page missing %q in body: %s", want, body) } } + if formEnd := strings.Index(body, ""); formEnd < 0 || !strings.Contains(body[formEnd:], `href="https://users.coulomb.social/security"`) { + t.Error("lost-authenticator help link must follow the OTP form and carry no request state") + } if mfa.validateCalls != 0 { t.Errorf("MFA token should not be validated until form submission, got %d calls", mfa.validateCalls) } diff --git a/workplans/KEY-WP-0038-forgejo-oidc-client.md b/workplans/KEY-WP-0038-forgejo-oidc-client.md index ea89b6f..45f9e06 100644 --- a/workplans/KEY-WP-0038-forgejo-oidc-client.md +++ b/workplans/KEY-WP-0038-forgejo-oidc-client.md @@ -9,7 +9,7 @@ flavor: implementation owner: claude-code topic_slug: railiance created: "2026-10-08" -updated: "2026-10-10" +updated: "2026-10-14" depends_on: [] related_repos: - prj-forgejo-org-refactor @@ -43,7 +43,7 @@ Helm release. out, never risk assets. Nothing in this plan changes who can read the forge today. It only makes the NetKingdom path available. -## Tasks +## T01 — Define what Forgejo receives ```task id: KEY-WP-0038-T01 @@ -52,8 +52,6 @@ priority: high state_hub_task_id: "5f019d13-1fa3-5b88-a27a-75a3339ca96e" ``` -### T01 — Define what Forgejo receives - Write the claim contract Forgejo consumes, as a non-secret request packet under `docs/`: @@ -89,6 +87,8 @@ so the agreement condition holds. Before T02, the LLDAP groups `forge-admins`, `forge-coulomb-owners` and `forge-coulomb-readers` must exist, with the founder in the first two. +## T02 — Register the client and hand over its secret + ```task id: KEY-WP-0038-T02 status: wait @@ -98,8 +98,6 @@ priority: high state_hub_task_id: "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30" ``` -### T02 — Register the client and hand over its secret - **Changed 2026-10-08 (founder direction):** no case-specific helper script. Registration is the first case of the SecurityCaseType `oidc-browser-relying-party` (net-kingdom NK-WP-0048, canon `security-case-types_v0.1`). The parameters @@ -130,6 +128,8 @@ check-config` (`c528daa`), which register-client runs before the restart. T02 st waits on NK-WP-0048-T06, which needs both executors' light paths (MASON-WP-0010-T03, WARDEN-WP-0044-T03). +## T03 — Prove the claims for each kind of identity + ```task id: KEY-WP-0038-T03 status: wait @@ -139,8 +139,6 @@ priority: high state_hub_task_id: "40ddaa29-7245-508c-ad48-ddc063e25115" ``` -### T03 — Prove the claims for each kind of identity - Before Forgejo uses the client, verify the issued ID token and UserInfo for one human (the founder, with MFA), one agent identity and one service identity: subject, `preferred_username`, `groups`, `principal_type`, assurance. Record the @@ -153,6 +151,8 @@ all, or receive scoped Forgejo tokens bound to a NetKingdom service identity Acceptance: evidence for all three principals; the agent and service decision is recorded. Then hand over to RAPPS-WP-0016. +## T04 — Implement the `keycape-client` entry point + ```task id: KEY-WP-0038-T04 status: cancel @@ -160,8 +160,6 @@ priority: high state_hub_task_id: "583cf1f1-16dc-515f-83bc-1efc45024a89" ``` -### T04 — Implement the `keycape-client` entry point - **Cancelled 2026-10-08: moved to net-kingdom NK-WP-0048-T09 (done).** The live registration machinery (contained kubectl, cluster and issuer pins) already lives in net-kingdom `sso-mfa/k8s/keycape/`, where KeyCape is deployed; a copy here diff --git a/workplans/KEY-WP-0039-openbao-negative-probe-tokens.md b/workplans/KEY-WP-0039-openbao-negative-probe-tokens.md index ffdf12f..7911fff 100644 --- a/workplans/KEY-WP-0039-openbao-negative-probe-tokens.md +++ b/workplans/KEY-WP-0039-openbao-negative-probe-tokens.md @@ -9,7 +9,7 @@ flavor: implementation owner: claude-code topic_slug: netkingdom created: "2026-10-10" -updated: "2026-10-11" +updated: "2026-10-14" depends_on: [] related_repos: - railiance-platform @@ -43,7 +43,7 @@ from `secrets-engine` in exactly one property each. - The probe script prints only accept or refuse booleans per case, never tokens or secrets. It runs attended. -## Tasks +## T01 — Define the probe registrations ```task id: KEY-WP-0039-T01 @@ -52,8 +52,6 @@ priority: medium state_hub_task_id: "4d06b059-3768-53bb-83a4-a9a71c0850b8" ``` -### T01 — Define the probe registrations - Write a non-secret request packet under `docs/` with one probe client per claim mismatch, each identical to `secrets-engine` except for: @@ -95,18 +93,19 @@ Condition (a), no OpenBao custody, holds. The values still have to reach KeyCape environment, because KeyCape resolves `secretRef` at startup. The packet uses a transient Kubernetes Secret that is deleted in the same session. +## T02 — Register the probe clients, run the attended probe, then remove them + ```task id: KEY-WP-0039-T02 -status: todo -depends_on: [KEY-WP-0039-T01] +status: wait +depends_on: [KEY-WP-0039-T01, KEY-WP-0038-T02, KEY-WP-0038-T03] +blocking_reason: "Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; registration authorization already resolved." priority: medium state_hub_task_id: "03da4c38-1dac-53b1-8b0b-e697df48e9d3" ``` 2026-10-11: requalified — live registration go caafe4be was already resolved; owner authorization present. See the-custodian docs/decisions/2026-10-11-security-unblock-decisions.md. -### T02 — Register the probe clients, run the attended probe, then remove them - Write the attended script, which prints booleans only. 2026-10-10: script written at `scripts/openbao_negative_probe.py`, with tests in @@ -327,3 +326,14 @@ remain done. No native or credential action is authorized by this source receipt See docs/proposals/2026-10-11-key39-next-execution-prerequisites.json for exact owner returns and safe metadata evidence needed before preparing a runnable attended execution proposal. + +2026-10-11 ordered-goal qualification (T02 only): the final source runner already +exists and its 17 local semantic tests pass. No new helper or source protocol is +needed to repeat accepted preparation. Native execution follows the first +Forgejo SecurityCase registration and claims proof (KEY-WP-0038-T02/T03); +only those task metadata were checked, not wait-task bodies. NetKingdom owns the +registry/env/restart session, KeyCape owns issuance/isolation, and Platform owns +leeway/refusal/capability/no-fallback/cleanup proof. The live registration go is +already recorded resolved; do not ask again. Attended human authentication and +owner-held existing coding-agent custody go on the single attended checklist. +No credentials, live registry/API changes or KEY-WP-0040 recovery work occurred. diff --git a/workplans/KEY-WP-0040-mfa-attended-run-findings.md b/workplans/KEY-WP-0040-mfa-attended-run-findings.md index d65929b..9074902 100644 --- a/workplans/KEY-WP-0040-mfa-attended-run-findings.md +++ b/workplans/KEY-WP-0040-mfa-attended-run-findings.md @@ -8,7 +8,7 @@ status: active owner: claude-code topic_slug: netkingdom created: "2026-10-11" -updated: "2026-10-12" +updated: "2026-10-14" flavor: implementation depends_on: [] state_hub_workstream_id: "a67f9d60-9b8f-5111-9fe5-9f64e631b1c3" @@ -86,3 +86,7 @@ factor-recovery-79e2964c112f3688 with the same CAS helper. Receipt and exact proposal: the-custodian/docs/evidence/2026-10-12-netkingdom-security/. T01 remains progress for the founder's authenticated tegwick preview and U08. T02 is a local source edit only; its source/image publication remains pending. + +2026-10-14: the MFA challenge render test now requires the lost-authenticator +link after the OTP form, with rel="noreferrer" and no request state in the URL. +T02 stays progress until that page is published and an attended journey sees it.