diff --git a/kubernetes/apps/authentik/blueprints-configmap.yaml b/kubernetes/apps/authentik/blueprints-configmap.yaml index a5c8f713..b333c8c1 100644 --- a/kubernetes/apps/authentik/blueprints-configmap.yaml +++ b/kubernetes/apps/authentik/blueprints-configmap.yaml @@ -612,3 +612,105 @@ data: identifiers: name: Caddy docs.renner.uno state: absent + + # Passkey (WebAuthn) step-up for selected apps. A dedicated + # AUTHORIZATION flow — unlike a per-provider authentication flow, the + # authorization flow runs on EVERY interactive authorize, so an + # existing password-only SSO session from another app cannot slip + # into the step-up apps without presenting a passkey. + passkey-stepup.yaml: | + version: 1 + metadata: + name: Passkey step-up authorization (homelab) + entries: + - id: passkey-stepup-flow + model: authentik_flows.flow + identifiers: + slug: passkey-stepup-authorization + state: present + attrs: + name: Passkey step-up authorization + title: "Redirecting to %(app)s" + designation: authorization + # Unauthenticated users are bounced through the default + # authentication flow first, then land back here for the + # WebAuthn validation — same as the built-in implicit-consent + # flow this replaces. + authentication: require_authenticated + + - id: passkey-stepup-webauthn-validation + model: authentik_stages_authenticator_validate.authenticatorvalidatestage + identifiers: + name: passkey-stepup-webauthn-validation + state: present + attrs: + device_classes: + - webauthn + # `configure`, not `deny`: g.renner has Home Assistant access + # but no enrolled WebAuthn device (live check 2026-07-07), so + # a hard deny would lock her out with no self-service path. + # `configure` injects the default WebAuthn enrollment stage + # inline — first login without a passkey becomes "enroll one + # now, then validate it". Still passkey-only: there is no way + # through this stage without a WebAuthn credential. + not_configured_action: configure + configuration_stages: + - !Find [authentik_stages_authenticator_webauthn.authenticatorwebauthnstage, [name, default-authenticator-webauthn-setup]] + # Skip re-validation if a passkey was already presented within + # the window. Non-zero for two reasons: (1) no double-touch + # when the login itself used a passkey seconds earlier; + # (2) NetBird's dashboard silently re-authorizes via + # /silent-auth (prompt=none) on token expiry — a flow that + # always demands interaction would fail those and force a + # visible re-login every access-token lifetime. + last_auth_threshold: hours=8 + webauthn_user_verification: preferred + + - model: authentik_flows.flowstagebinding + identifiers: + target: !KeyOf passkey-stepup-flow + stage: !KeyOf passkey-stepup-webauthn-validation + order: 10 + state: present + + # Re-point the (previously UI-managed) OAuth2 providers at the + # step-up flow. Blueprint apply validates the full serializer, so + # the required fields (invalidation_flow, redirect_uris) mirror + # the live values as of 2026-07-07 — git owns them from here on; + # UI edits to these fields will be reverted on the next apply + # cycle. + - model: authentik_providers_oauth2.oauth2provider + identifiers: + name: Homeassistant + state: present + attrs: + authorization_flow: !KeyOf passkey-stepup-flow + invalidation_flow: !Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]] + client_type: confidential + redirect_uris: + - url: https://home.renner.uno/auth/openid/callback + matching_mode: strict + - url: http://homeassistant.srv.renner.uno:8123/auth/openid/callback + matching_mode: strict + + - model: authentik_providers_oauth2.oauth2provider + identifiers: + name: Provider for Netbird + state: present + attrs: + authorization_flow: !KeyOf passkey-stepup-flow + invalidation_flow: !Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]] + client_type: public + redirect_uris: + - url: http://localhost:53000 + matching_mode: strict + - url: https://netbird.renner.uno/ + matching_mode: strict + - url: https://netbird.renner.uno/auth + matching_mode: strict + - url: https://netbird.renner.uno/silent-auth + matching_mode: strict + - url: https://netbird.renner.uno/peers + matching_mode: strict + - url: 'https://netbird\.renner\.uno(/.*)?' + matching_mode: regex diff --git a/kubernetes/apps/authentik/helmrelease.yaml b/kubernetes/apps/authentik/helmrelease.yaml index e5f4f22e..0c23b743 100644 --- a/kubernetes/apps/authentik/helmrelease.yaml +++ b/kubernetes/apps/authentik/helmrelease.yaml @@ -160,10 +160,10 @@ spec: redis: enabled: false - # Mount the K8s-outpost-integration blueprint so the worker re-points - # every Outpost at the in-cluster Kubernetes service connection on - # boot and drops the legacy Docker connection inherited from the - # Proxmox deployment. See blueprints-configmap.yaml. + # Mount the homelab blueprints (K8s outpost integration, passkey + # step-up authorization) — every key of the ConfigMap is applied by + # the worker on boot and on each apply cycle. See + # blueprints-configmap.yaml. blueprints: configMaps: - authentik-blueprints-k8s-outposts