# Optional MFA for browser clients Set `mfaOptional: true` on a reviewed browser registration to allow AAL1 login when privacyIDEA authoritatively reports no active enrolled factor. Once an active factor exists, require AAL2. A previous AAL1 session cannot satisfy that requirement. Explicit AAL2 requests still require MFA. Lookup errors deny login through the existing account-recovery path. This setting overrides only the provider `requireForAll` default for that client. Do not combine this with `mfaRequired`. In particular, `mfaRequired: false` means unconditional AAL1 and is not an opt-in enrollment policy. Unchanged registrations keep their existing policy. Service clients reject mfaOptional. The adapter sends the raw privacyIDEA JWT in Authorization, filters active factors before pagination, and requires a successful, complete response before concluding there is no active factor. The credential must have realm-scoped administrative tokenlist permission: a user-role token only lists its own factors regardless of the requested username. Never replace it with a self-service JWT. See the [provider API](https://privacyidea.readthedocs.io/en/stable/modules/api/token.html). ## Deployed policy and guarded onboarding P06 is deployed for `vergabe-demo-company` and `user-engine-portal` only. The issuer owns persistent runtime overrides in `KEYCAPE_POLICY_PATH` on the `keycape-authentication-policy` PVC. Use the portal Authentication policy page with a recent-MFA platform session for preview, acknowledged apply and rollback. Explicit application AAL2 always wins. Administration requires MFA even when ordinary portal sign-in is optional until factor activation. privacyIDEA self-service allows TOTP enrollment and deletion only for unfinished verification-state setup. Possession confirmation activates the factor. An active factor cannot be deleted, disabled or replaced from a password-only provider session; use audited platform recovery. Both native issuer and installed-provider browser acceptance passed against disposable identities and databases. Factor-read custody, automatic renewal and mounted-file delivery use RPF-WP-0040 / CCR-2026-0023. No-factor decisions require the realm visibility probe. Provider lookup/credential failures deny login; pending factor state does not activate MFA. Validation requires actual TOTP/HOTP evidence, so provider password passthrough cannot establish AAL2. Release, exact image pins, positive test coverage and rollback order: `../user-engine/docs/evidence/2026-09-13-p06-authentication-policy.md`. Actual invited-person and application login remains KEY-WP-0034 and VERGABE-WP-0019. Keep the policy PVC and provider guard during replacement. Before reverting pre-P06 portal guards, restore and verify mandatory policy for both clients in runtime and baseline registration. ## Credential renewal consumer contract KeyCape accepts either `privacyidea.adminToken` (existing startup configuration) or `privacyidea.adminTokenFile` (an absolute protected mounted-file path). Configure one source only. With a file, KeyCape reads the current value for each factor lookup and OTP validation. Deliver renewal atomically; do not truncate a live file in place. A projected Secret volume may update asynchronously; a `subPath` mount will not provide live renewal. The deployment owner must verify propagation. The file contains only the raw provider JWT with an optional trailing newline; input is bounded to 16 KiB. Missing, empty, malformed, oversized or unreadable files deny the operation without a cached or inline fallback. File contents and paths are excluded from credential errors. Provider rejection also denies access; file presence alone does not prove credential validity, scope or freshness. Default provider requests have a ten-second timeout and do not follow redirects. The credential must remain provider-issued, realm-scoped and renewed through owner custody. This consumer feature neither mints credentials nor establishes an OpenBao path. Deploying an approved file requires a separate protected mount, read permission for KeyCape's runtime identity, renewal/expiry ownership, and positive factor/no-factor plus failed-credential/recovery evidence. The dedicated delivery lane is deployed; historical resolver credential repair remains separately owned under NK-WP-0033. ## Distinguish missing factors from revoked reader access The installed provider returns HTTP 200 with zero tokens after tokenlist policy withdrawal. Before accepting a no-factor result, KeyCape therefore requires a second exact-serial lookup for `privacyidea.readProbeSerial`. This must identify a disabled, unassigned token in exactly the configured realm. Missing probe, missing visibility, assignment, activation, realm mismatch or provider failure denies the decision. Existing confirmed factors still require MFA. The production probe is `KCFACTORSCOPE01`, created idempotently by NetKingdom's `sso-mfa/k8s/privacyidea/keycape-scope-probe.py`. Its key is generated by the provider and never delivered to KeyCape. Keep it disabled and unassigned; it exists solely to prove realm visibility. Removing it intentionally makes no-factor decisions unavailable until scope proof is restored. `provider-onboarding-contract.py` uses a temporary in-memory database and no production configuration or credentials. Against the installed provider it proved password-only pass-through, pending-active enrollment, cancellation, TOTP possession confirmation (explicit `type=totp` on confirmation), confirmed state, empty-success on permission withdrawal, permission recovery and rejection of a genuinely provider-issued expired JWT. Production expiry/revocation drills remain separately recorded under RPF-WP-0040-T04.