diff --git a/docs/adr/ADR-0016-mfa-user-preference-and-workload-step-up.md b/docs/adr/ADR-0016-mfa-user-preference-and-workload-step-up.md new file mode 100644 index 0000000..d53dea7 --- /dev/null +++ b/docs/adr/ADR-0016-mfa-user-preference-and-workload-step-up.md @@ -0,0 +1,75 @@ +--- +id: NK-ADR-0016 +type: architecture-decision-record +title: "MFA Is a User Preference by Default; Workloads Opt In to Requiring It" +status: accepted +owner: net-kingdom +revision: "1" +decided: "2026-09-23" +last_reviewed: "2026-09-23" +review_interval: 12m +--- + +# ADR-0016 - MFA Is a User Preference by Default; Workloads Opt In to Requiring It + +**Status:** Accepted +**Date:** 2026-09-23 +**Deciders:** Bernd Worsch, Claude + +## Context + +The attended Vergabe recipient journey on 2026-09-23 (NK-WP-0036-T05, +NK-WP-0037-T02) completed sign-in and account confirmation without MFA. +KeyCape policy P06 (`key-cape/docs/optional-mfa.md`, deployed 2026-09-14 for +`vergabe-demo-company` and `user-engine-portal`) allowed this. Under P06, AAL1 +is permitted when privacyIDEA authoritatively reports no active factor, and +AAL2 is required once one exists. + +NK-WP-0037 had been written on 2026-09-12 as "provider-required MFA". The +operator decided against requiring MFA for Vergabe Teilnahme now. The +enrollment and recovery journeys (user-engine U06, factor recovery) have not +yet been accepted from a user's perspective, and requiring MFA before they +work pushes users into support paths. + +## Decision + +1. **Default: MFA is the user's preference.** For ordinary human sign-in to a + workload, MFA is offered but not required. A user who activates a factor + is held to it: later sign-ins require AAL2, and a prior AAL1 session does + not satisfy it (P06 semantics). +2. **Workloads opt in to requiring MFA,** for all of their features or for + some of them. A workload states the requirement explicitly, either per + client registration or per request as a step-up (an explicit AAL2 request + at the moment a protected feature is used). An explicit workload + requirement always wins over a user's preference. Authorization decisions + read it from the normalized `assurance.level` claim (IAM Profile v0.3), + never from a provider-native claim. +3. **The IAM Profile v0.3 floor is unchanged.** Privileged, destructive, + platform-root, secret, credential-vending and emergency flows require AAL2 + or stronger, and administration requires MFA even where ordinary sign-in + does not. This ADR relaxes nothing below that floor. +4. **Usability comes before a mandatory requirement.** The enrollment, + re-enrollment and recovery journeys must be accepted from a user's + perspective before any workload's default sign-in is switched to + required MFA. + +## Consequences + +- Vergabe Teilnahme keeps P06 optional MFA. NK-WP-0037's "provider-required + MFA" wording is superseded. +- A workload that needs MFA for part of its feature set needs a step-up path: + it requests AAL2 when a protected feature is used, and KeyCape answers with + an MFA challenge that its existing session cannot bypass. The KeyCape and + application contract for that step-up is follow-up work (NK-WP-0042, + proposed). +- The user-engine enrollment journey (U06) and factor recovery are + prerequisites for any workload making MFA mandatory by default. +- A per-client `mfaRequired: false` remains unconditional AAL1 and is not a + preference mechanism. Use P06 `mfaOptional` for preference-based clients. + +## Related + +- IAM Profile v0.3 (Human Interactive Flow; Assurance Evidence) +- `key-cape/docs/optional-mfa.md` (P06) +- NK-WP-0036, NK-WP-0037, NK-WP-0041 +- user-engine account journeys U06 (voluntary authenticator activation) diff --git a/workplans/NK-WP-0037-vergabe-company-welcome.md b/workplans/NK-WP-0037-vergabe-company-welcome.md index 031ce02..27f964c 100644 --- a/workplans/NK-WP-0037-vergabe-company-welcome.md +++ b/workplans/NK-WP-0037-vergabe-company-welcome.md @@ -4,7 +4,7 @@ type: workplan title: "Bind password setup to the Vergabe company welcome and sign-in" domain: infotech repo: net-kingdom -status: active +status: finished flavor: implementation owner: codex topic_slug: netkingdom @@ -38,7 +38,7 @@ Existing setup links remain process-local and expire on restart. ```task id: NK-WP-0037-T02 -status: wait +status: done priority: high state_hub_task_id: "8921691b-e7a2-543c-8189-3abc24de1bc7" ``` @@ -114,3 +114,28 @@ are two candidate fixes: T02 waits on key-cape. MFA enrollment and account confirmation were not reached. Rerun the same recipient once the fix is live. + +### Attended rerun 2026-09-23 — T02 done + +key-cape shipped the fix under KEY-WP-0033-T02 (`key-cape@11ce29a`, image +`key-cape@sha256:8dc35801…`). The operator rolled it out on railiance01, and +it is pinned at net-kingdom `8ee0f8b`. Post-rollout checks: + +- `verify-openbao-client.sh` passed. +- The Vergabe callback with PKCE returned 302; a wrong callback and missing + PKCE each returned 400. +- `prompt=login` now reaches Authelia as `max_age=10`. + +Recipient `bernd.worsch-99` then completed the journey in a new private +window: + +- KeyCape `auth_start` at 20:16:36, `auth_success` at 20:17:28 and + `token_issued` at 20:17:29 for `vergabe-demo-company`. +- Vergabe logged `/accounts/oidc/callback/` 302, the confirmation page 200, + and the confirmation POST 302. + +No MFA was involved, by design. P06 permits AAL1 when privacyIDEA +authoritatively reports no active factor; privacyIDEA logged the no-factor +token lookup at 20:17:28. The operator decided that MFA stays a user +preference for Vergabe Teilnahme for now (ADR-0016). That supersedes the +"provider-required MFA" wording above. Workload-requested MFA is NK-WP-0042. diff --git a/workplans/NK-WP-0042-workload-mfa-step-up.md b/workplans/NK-WP-0042-workload-mfa-step-up.md new file mode 100644 index 0000000..617b25c --- /dev/null +++ b/workplans/NK-WP-0042-workload-mfa-step-up.md @@ -0,0 +1,53 @@ +--- +id: NK-WP-0042 +type: workplan +title: "Let workloads require MFA for all or part of their features" +domain: infotech +repo: net-kingdom +status: proposed +flavor: planning +owner: claude-code +topic_slug: netkingdom +created: "2026-09-23" +updated: "2026-09-23" +related: [NK-ADR-0016, NK-WP-0037] +--- + +ADR-0016 makes MFA a user preference by default and lets a workload require +it for all or some of its features. This workplan defines how a workload +states that requirement and how the flow enforces it. It does not require MFA +anywhere; the enrollment and recovery usability gate in ADR-0016 still +applies first. + +## Specify the workload-requested step-up contract + +```task +id: NK-WP-0042-T01 +status: todo +priority: medium +``` + +Define how a workload requests AAL2: per client registration for the whole +feature set, or per request as a step-up when a protected feature is used. +Specify: + +- which OIDC parameter the request uses (`acr_values`, `max_age`, or both); +- what KeyCape must return when a user has no factor (a clean refusal and an + enrollment route, never silent AAL1); +- how the resulting `assurance.level` reaches flex-auth. + +Record it as an IAM Profile amendment. key-cape owns the implementation. + +## Agree the user-facing step-up and enrollment journey + +```task +id: NK-WP-0042-T02 +status: todo +priority: medium +``` + +With user-engine (U06, factor recovery) and one pilot workload, agree what a +user sees when a feature needs MFA and they have none. That includes the +enrollment detour, the return to the feature, and recovery. It must be +accepted from a user's perspective before any workload turns the requirement +on.