From 250d0fc334d221f58d3b05737ac26a37777b6c96 Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Sun, 28 Jun 2026 12:24:40 +0000 Subject: [PATCH] docs(authentik): document SFE forced-WebAuthn escape hatches (TOTP + social) Old-browser users on the SFE who have a password but no MFA device hit the default-authentication-flow's forced WebAuthn passkey enrolment, which the SFE cannot render (the 'unsupported state: ak-stage-authenticator-webauthn' error). emo (Google-only, iPadOS 15) hit this on the password path. Document the two no-MFA-downgrade fixes: (1) social login, whose source flow (default-source-authentication) has no MFA stage, so the SFE's social button always completes; (2) enrolling TOTP, which the SFE can validate (unlike WebAuthn) and which flips the MFA stage from force-enrol to validate. TOTP was enrolled for emo and stored in his Vaultwarden authentik item; verified end-to-end (a Bitwarden-generated code is accepted by authentik). Co-Authored-By: Claude Opus 4.8 --- docs/architecture/authentication.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/architecture/authentication.md b/docs/architecture/authentication.md index dc773b81..620bcf6b 100644 --- a/docs/architecture/authentication.md +++ b/docs/architecture/authentication.md @@ -123,6 +123,19 @@ Signin latency is dominated by screen count and round trips, not server time basic-auth fallback was rejected: it would have put a single spoofable-UA password in front of `vbarzin→wizard` (passwordless root on the devvm). See `stacks/authentik/patch-compat-sfe.py`. +- **SFE + forced-WebAuthn MFA gotcha** (2026-06-28): the `default-authentication-flow` + MFA stage (`not_configured_action=configure`, `conf_stages=[webauthn]`) force-enrols + a WebAuthn passkey for any **password**-path user with no MFA device — but the SFE + **cannot render WebAuthn** (enrol *or* validate), so that user gets + `unsupported state: ak-stage-authenticator-webauthn`. Two escape hatches, **no MFA + downgrade**: (1) **social login** — sources run `default-source-authentication` + (UserLoginStage only, **no MFA stage**), so the SFE's "Continue with " + button always completes; (2) **enrol TOTP** — the SFE *can* validate TOTP codes, and + ≥1 confirmed device flips the stage from force-enrol to validate. User MFA devices are + runtime data (not Terraform): enrol via `ak shell` + (`TOTPDevice.objects.create(user=…, confirmed=True)`) and store the secret in the + user's own Vaultwarden item. (Done for emo — the Google-only iPadOS-15 case: TOTP in + his `authentik.viktorbarzin.me` Bitwarden item; e2e-verified the BW code is accepted.) - **Outpost**: 2 replicas, `log_level=info` (was 1 replica at `trace`). - **auth-proxy nginx**: upstream `keepalive 32` + HTTP/1.1 — no per-request TCP setup on the forward-auth subrequest path.