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`} +
+ + + +
+
`; } }