diff --git a/authentik/stages/authenticator_webauthn/api.py b/authentik/stages/authenticator_webauthn/api.py
index 7c7d4be54..57d04cbc8 100644
--- a/authentik/stages/authenticator_webauthn/api.py
+++ b/authentik/stages/authenticator_webauthn/api.py
@@ -18,7 +18,7 @@ class AuthenticateWebAuthnStageSerializer(StageSerializer):
class Meta:
model = AuthenticateWebAuthnStage
- fields = StageSerializer.Meta.fields + ["configure_flow"]
+ fields = StageSerializer.Meta.fields + ["configure_flow", "user_verification"]
class AuthenticateWebAuthnStageViewSet(UsedByMixin, ModelViewSet):
diff --git a/authentik/stages/authenticator_webauthn/models.py b/authentik/stages/authenticator_webauthn/models.py
index 708270f4a..c0439501c 100644
--- a/authentik/stages/authenticator_webauthn/models.py
+++ b/authentik/stages/authenticator_webauthn/models.py
@@ -9,15 +9,36 @@ from django.views import View
from django_otp.models import Device
from rest_framework.serializers import BaseSerializer
from webauthn.helpers.base64url_to_bytes import base64url_to_bytes
-from webauthn.helpers.structs import PublicKeyCredentialDescriptor
+from webauthn.helpers.structs import PublicKeyCredentialDescriptor, UserVerificationRequirement
from authentik.core.types import UserSettingSerializer
from authentik.flows.models import ConfigurableStage, Stage
+class UserVerification(models.TextChoices):
+ """The degree to which the Relying Party wishes to verify a user's identity.
+
+ Members:
+ `REQUIRED`: User verification must occur
+ `PREFERRED`: User verification would be great, but if not that's okay too
+ `DISCOURAGED`: User verification should not occur, but it's okay if it does
+
+ https://www.w3.org/TR/webauthn-2/#enumdef-userverificationrequirement
+ """
+
+ REQUIRED = UserVerificationRequirement.REQUIRED
+ PREFERRED = UserVerificationRequirement.PREFERRED
+ DISCOURAGED = UserVerificationRequirement.DISCOURAGED
+
+
class AuthenticateWebAuthnStage(ConfigurableStage, Stage):
"""WebAuthn stage"""
+ user_verification = models.TextField(
+ choices=UserVerification.choices,
+ default=UserVerification.PREFERRED,
+ )
+
@property
def serializer(self) -> BaseSerializer:
from authentik.stages.authenticator_webauthn.api import AuthenticateWebAuthnStageSerializer
diff --git a/authentik/stages/authenticator_webauthn/stage.py b/authentik/stages/authenticator_webauthn/stage.py
index 53a192626..d9c5a9ebb 100644
--- a/authentik/stages/authenticator_webauthn/stage.py
+++ b/authentik/stages/authenticator_webauthn/stage.py
@@ -14,7 +14,6 @@ from webauthn.helpers.structs import (
PublicKeyCredentialCreationOptions,
RegistrationCredential,
ResidentKeyRequirement,
- UserVerificationRequirement,
)
from webauthn.registration.verify_registration_response import VerifiedRegistration
@@ -27,7 +26,7 @@ from authentik.flows.challenge import (
)
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
from authentik.flows.stage import ChallengeStageView
-from authentik.stages.authenticator_webauthn.models import WebAuthnDevice
+from authentik.stages.authenticator_webauthn.models import AuthenticateWebAuthnStage, WebAuthnDevice
from authentik.stages.authenticator_webauthn.utils import get_origin, get_rp_id
LOGGER = get_logger()
@@ -83,7 +82,7 @@ class AuthenticatorWebAuthnStageView(ChallengeStageView):
def get_challenge(self, *args, **kwargs) -> Challenge:
# clear session variables prior to starting a new registration
self.request.session.pop("challenge", None)
-
+ stage: AuthenticateWebAuthnStage = self.executor.current_stage
user = self.get_pending_user()
registration_options: PublicKeyCredentialCreationOptions = generate_registration_options(
@@ -94,7 +93,7 @@ class AuthenticatorWebAuthnStageView(ChallengeStageView):
user_display_name=user.name,
authenticator_selection=AuthenticatorSelectionCriteria(
resident_key=ResidentKeyRequirement.PREFERRED,
- user_verification=UserVerificationRequirement.PREFERRED,
+ user_verification=str(stage.user_verification),
),
)
registration_options.user.id = user.uid
diff --git a/schema.yml b/schema.yml
index 39abf799e..782cf6a0e 100644
--- a/schema.yml
+++ b/schema.yml
@@ -15395,6 +15395,14 @@ paths:
schema:
type: string
format: uuid
+ - in: query
+ name: user_verification
+ schema:
+ type: string
+ enum:
+ - discouraged
+ - preferred
+ - required
tags:
- stages
security:
@@ -19240,6 +19248,8 @@ components:
nullable: true
description: Flow used by an authenticated user to configure this Stage.
If empty, user will not be able to configure this stage.
+ user_verification:
+ $ref: '#/components/schemas/UserVerificationEnum'
required:
- component
- meta_model_name
@@ -19264,6 +19274,8 @@ components:
nullable: true
description: Flow used by an authenticated user to configure this Stage.
If empty, user will not be able to configure this stage.
+ user_verification:
+ $ref: '#/components/schemas/UserVerificationEnum'
required:
- name
AuthenticatedSession:
@@ -26611,6 +26623,8 @@ components:
nullable: true
description: Flow used by an authenticated user to configure this Stage.
If empty, user will not be able to configure this stage.
+ user_verification:
+ $ref: '#/components/schemas/UserVerificationEnum'
PatchedAuthenticatorDuoStageRequest:
type: object
description: AuthenticatorDuoStage Serializer
@@ -31235,6 +31249,12 @@ components:
- pk
- source
- user
+ UserVerificationEnum:
+ enum:
+ - required
+ - preferred
+ - discouraged
+ type: string
UserWriteStage:
type: object
description: UserWriteStage Serializer
diff --git a/web/src/locales/en.po b/web/src/locales/en.po
index f2b8fa1d6..e30ae36a3 100644
--- a/web/src/locales/en.po
+++ b/web/src/locales/en.po
@@ -4325,6 +4325,7 @@ msgstr "Stage(s)"
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
+#: src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
#: src/pages/stages/captcha/CaptchaStageForm.ts
#: src/pages/stages/consent/ConsentStageForm.ts
#: src/pages/stages/email/EmailStageForm.ts
@@ -5510,6 +5511,22 @@ msgstr "User password writeback"
msgid "User status"
msgstr "User status"
+#: src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
+msgid "User verification"
+msgstr "User verification"
+
+#: src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
+msgid "User verification is preferred if available, but not required."
+msgstr "User verification is preferred if available, but not required."
+
+#: src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
+msgid "User verification must occur."
+msgstr "User verification must occur."
+
+#: src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
+msgid "User verification should not occur."
+msgstr "User verification should not occur."
+
#: src/pages/events/utils.ts
msgid "User was written to"
msgstr "User was written to"
diff --git a/web/src/locales/fr_FR.po b/web/src/locales/fr_FR.po
index e62b30b61..754a59ff2 100644
--- a/web/src/locales/fr_FR.po
+++ b/web/src/locales/fr_FR.po
@@ -4285,6 +4285,7 @@ msgstr "Étape(s)"
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
+#: src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
#: src/pages/stages/captcha/CaptchaStageForm.ts
#: src/pages/stages/consent/ConsentStageForm.ts
#: src/pages/stages/email/EmailStageForm.ts
@@ -5448,6 +5449,22 @@ msgstr "Réécriture du mot de passe utilisateur"
msgid "User status"
msgstr "Statut utilisateur"
+#: src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
+msgid "User verification"
+msgstr ""
+
+#: src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
+msgid "User verification is preferred if available, but not required."
+msgstr ""
+
+#: src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
+msgid "User verification must occur."
+msgstr ""
+
+#: src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
+msgid "User verification should not occur."
+msgstr ""
+
#: src/pages/events/utils.ts
msgid "User was written to"
msgstr "L'utilisateur a été écrit vers "
diff --git a/web/src/locales/pseudo-LOCALE.po b/web/src/locales/pseudo-LOCALE.po
index 1fd9a7f09..8bc3e5c17 100644
--- a/web/src/locales/pseudo-LOCALE.po
+++ b/web/src/locales/pseudo-LOCALE.po
@@ -4315,6 +4315,7 @@ msgstr ""
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
+#: src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
#: src/pages/stages/captcha/CaptchaStageForm.ts
#: src/pages/stages/consent/ConsentStageForm.ts
#: src/pages/stages/email/EmailStageForm.ts
@@ -5490,6 +5491,22 @@ msgstr ""
msgid "User status"
msgstr ""
+#: src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
+msgid "User verification"
+msgstr ""
+
+#: src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
+msgid "User verification is preferred if available, but not required."
+msgstr ""
+
+#: src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
+msgid "User verification must occur."
+msgstr ""
+
+#: src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
+msgid "User verification should not occur."
+msgstr ""
+
#: src/pages/events/utils.ts
msgid "User was written to"
msgstr ""
diff --git a/web/src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts b/web/src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
index 96e52921f..c1193d5b7 100644
--- a/web/src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
+++ b/web/src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
@@ -1,3 +1,5 @@
+import { UserVerificationEnum } from "@goauthentik/api/dist/models/UserVerificationEnum";
+
import { t } from "@lingui/macro";
import { TemplateResult, html } from "lit";
@@ -52,6 +54,40 @@ export class AuthenticateWebAuthnStageForm extends ModelForm
+
+ ${t`Stage-specific settings`}
+
+
`;
}
}