key-cape/workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md
codex 270a065383 fix(workplans): adopt ADR-007 derived identifiers for unregistered records
These workplans exist only in the retired local hub. Their random pre-ADR-007
identifiers are refused by C-06 as stale references, so they cannot be
registered. Deriving from the canonical record id takes no identity from
anything: central does not hold them and the old ids die with the cache.

Records central already holds were deliberately left untouched.

Refs CUST-WP-0068-T06

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-25 20:13:23 +02:00

4.5 KiB

id type title domain repo status owner topic_slug created updated depends_on state_hub_workstream_id
KEY-WP-0008 workplan Registration handoff and client-aware MFA policy infotech key-cape finished grok netkingdom 2026-08-09 2026-08-16
NK-WP-0025
23eb67d4-cf10-57a1-92f6-90146a294bda

KEY-WP-0008 - registration handoff and client-aware MFA

Let a registered OIDC client offer NetKingdom signup and request step-up without making KeyCape an account store or weakening high-assurance clients.

T01 - Add a safe registration handoff

id: KEY-WP-0008-T01
status: done
priority: high
state_hub_task_id: "e5fb4d73-1e19-565c-80d5-e0eee274ce63"

Define an allow-listed registration URL for eligible clients and preserve client ID, redirect URI, PKCE intent, tenant hint, and return context in a signed, expiring state envelope. Registration completion must restart the normal authorization flow and must not mint a token directly.

Done when unknown users can choose signup from an eligible authorization flow without open redirect, client substitution, or state replay.

Implemented prompt=create and /authorize/register against the client's static registrationUrl, plus HMAC-signed kc_handoff envelopes consumed once at /authorize/return. Return restarts /authorize and never mints a code. Ineligible clients get no signup link. Live registration entry remains user-engine-owned per NK-WP-0025; KeyCape only issues the return envelope.

T02 - Replace global MFA with client-aware minimum assurance

id: KEY-WP-0008-T02
status: done
priority: high
state_hub_task_id: "1ba50454-b6f5-578e-a526-da74b8082a89"

Replace the single require-for-all switch with policy that combines client minimum assurance, requested ACR/step-up, tenant policy, protected action, and current session assurance. Preserve mandatory MFA for platform/admin clients. Allow coulomb-social ordinary login at password assurance when no stronger rule applies.

Done when one low-assurance client cannot suppress MFA for another client or reuse an under-assured session for a high-assurance request.

Implemented with nullable per-client mfaRequired: an explicit client value overrides the provider default only for that client. Absent values preserve the existing provider-driven policy.

2026-08-16: DecideAssurance now combines client minimum, requested ACR, provider default, max_age, prompt=login, and current KeyCape login-session level. An AAL1 session cannot satisfy an AAL2 client or acr_values=aal2. coulomb-social in config/dev-config.yaml is mfaRequired: false; other clients keep the provider default. Users without an enrolled factor are sent to the client's enrollmentUrl instead of completing authorization.

T03 - Support explicit step-up and fresh authentication

id: KEY-WP-0008-T03
status: done
priority: high
state_hub_task_id: "f7acbc12-e597-5d1e-8b29-bfa02237730f"

Implement supported ACR/max-age or equivalent IAM Profile parameters, invoke privacyIDEA only when policy requires it, and return verifiable assurance claims. Handle users without an enrolled factor through a safe enrollment handoff rather than an authorization bypass.

Done when coulomb.social can request MFA for a profile/action and verify the result from token claims.

Implemented acr_values preservation and AAL2/MFA forcing through the privacyIDEA challenge. The existing token assurance claim reports aal2 and mfa: true only after successful verification.

T04 - Prove policy isolation and compatibility

id: KEY-WP-0008-T04
status: done
priority: high
state_hub_task_id: "bc660767-387e-59a5-bfce-caccf4082c08"

Test known and unknown users, registration link eligibility, state expiry and replay, password-only coulomb-social login, profile/action step-up, no-factor enrollment, OpenBao mandatory MFA, cross-client session reuse, and logout. Keep static client registration and exact redirect rules unchanged.

Done when existing high-assurance clients pass unchanged and the new coulomb-social journey passes live.

2026-08-16: isolation tests cover known/unknown users, registration eligibility, state expiry/replay, password-only coulomb-social, ACR step-up, no-factor enrollment handoff, OpenBao mandatory MFA, cross-client AAL1 session reuse, logout, and exact redirect enforcement. Full Go suite passes. Live coulomb-social AAL1/AAL2 isolation was already proven on railiance01 under NK-WP-0025-T05 (2026-08-14); this closeout adds the KeyCape-side regression suite and /logout.