From fb6ac12cbe4ffd7d3999a62c811c6f24b159bee7 Mon Sep 17 00:00:00 2001 From: Konstantin Renner Date: Tue, 7 Jul 2026 10:01:37 +0200 Subject: [PATCH] authentik: Passkey-Autofill im Passwordless-Flow (kein Username-Tippen) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Setzt webauthn_stage auf der Identification-Stage des Passwordless- Flows (Conditional WebAuthn, authentik >= 2025.12): der Browser bietet enrollte Passkeys direkt im Autofill des Username-Felds an — ein Tap, kein Username. Username-Tippen bleibt als Fallback für nicht- discoverable Credentials erhalten. last_auth_threshold der Passwordless-Validation-Stage von seconds=0 auf minutes=5: verhindert Doppel-Challenge derselben Flow-Ausführung nach Autofill-Validierung; der Skip ist session-gebunden. Validation-Stage-Entry vor den Identification-Entry gezogen, weil !KeyOf nur rückwärts auflöst. Co-Authored-By: Claude Fable 5 --- .../apps/authentik/blueprints-configmap.yaml | 38 +++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/kubernetes/apps/authentik/blueprints-configmap.yaml b/kubernetes/apps/authentik/blueprints-configmap.yaml index 5a49c80b..868f5e36 100644 --- a/kubernetes/apps/authentik/blueprints-configmap.yaml +++ b/kubernetes/apps/authentik/blueprints-configmap.yaml @@ -690,16 +690,6 @@ data: title: "Sign in with your passkey" designation: authentication - - id: passkey-passwordless-identification - model: authentik_stages_identification.identificationstage - identifiers: - name: passkey-passwordless-identification - state: present - attrs: - user_fields: - - username - - email - # SEPARATE stage from the step-up one, and deliberately `deny`: # at this point in an authentication flow the user has only been # *identified* (typed a username), not authenticated — `configure` @@ -710,6 +700,10 @@ data: # fully authenticated session. A passkey-less user therefore # bootstraps via password login on auth.renner.uno, then gets the # enrollment prompt on first app access. + # + # Declared BEFORE the identification stage because the latter + # references it via !KeyOf (webauthn_stage), and the blueprint + # importer resolves entries top-to-bottom. - id: passkey-passwordless-webauthn-validation model: authentik_stages_authenticator_validate.authenticatorvalidatestage identifiers: @@ -719,9 +713,31 @@ data: device_classes: - webauthn not_configured_action: deny - last_auth_threshold: seconds=0 + # Small non-zero window so a passkey presented via the + # identification stage's autofill (webauthn_stage below) is + # not challenged a second time by this stage at order 20 in + # the same flow run. The skip is session-scoped, so a fresh + # (attacker) session never inherits it. + last_auth_threshold: minutes=5 webauthn_user_verification: preferred + # webauthn_stage enables passkey autofill (conditional WebAuthn, + # authentik >= 2025.12): the browser offers enrolled passkeys in + # the username field's autofill — one tap, no username typed. + # Requires a discoverable (resident) credential; users whose + # credential is non-discoverable simply type their username and + # get the regular WebAuthn challenge at order 20 instead. + - id: passkey-passwordless-identification + model: authentik_stages_identification.identificationstage + identifiers: + name: passkey-passwordless-identification + state: present + attrs: + user_fields: + - username + - email + webauthn_stage: !KeyOf passkey-passwordless-webauthn-validation + - model: authentik_flows.flowstagebinding identifiers: target: !KeyOf passkey-passwordless-flow -- 2.54.0