Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a09cbb-87c6-7900-a145-4ce53ba9f1a6
93 lines
5.6 KiB
Markdown
93 lines
5.6 KiB
Markdown
# 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.
|