stages/authenticator_webauthn: make user_verification configurable

closes #1921

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-12-14 09:58:20 +01:00
parent aa6b595545
commit 4e6714fffe
8 changed files with 133 additions and 6 deletions

View file

@ -18,7 +18,7 @@ class AuthenticateWebAuthnStageSerializer(StageSerializer):
class Meta: class Meta:
model = AuthenticateWebAuthnStage model = AuthenticateWebAuthnStage
fields = StageSerializer.Meta.fields + ["configure_flow"] fields = StageSerializer.Meta.fields + ["configure_flow", "user_verification"]
class AuthenticateWebAuthnStageViewSet(UsedByMixin, ModelViewSet): class AuthenticateWebAuthnStageViewSet(UsedByMixin, ModelViewSet):

View file

@ -9,15 +9,36 @@ from django.views import View
from django_otp.models import Device from django_otp.models import Device
from rest_framework.serializers import BaseSerializer from rest_framework.serializers import BaseSerializer
from webauthn.helpers.base64url_to_bytes import base64url_to_bytes 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.core.types import UserSettingSerializer
from authentik.flows.models import ConfigurableStage, Stage 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): class AuthenticateWebAuthnStage(ConfigurableStage, Stage):
"""WebAuthn stage""" """WebAuthn stage"""
user_verification = models.TextField(
choices=UserVerification.choices,
default=UserVerification.PREFERRED,
)
@property @property
def serializer(self) -> BaseSerializer: def serializer(self) -> BaseSerializer:
from authentik.stages.authenticator_webauthn.api import AuthenticateWebAuthnStageSerializer from authentik.stages.authenticator_webauthn.api import AuthenticateWebAuthnStageSerializer

View file

@ -14,7 +14,6 @@ from webauthn.helpers.structs import (
PublicKeyCredentialCreationOptions, PublicKeyCredentialCreationOptions,
RegistrationCredential, RegistrationCredential,
ResidentKeyRequirement, ResidentKeyRequirement,
UserVerificationRequirement,
) )
from webauthn.registration.verify_registration_response import VerifiedRegistration 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.planner import PLAN_CONTEXT_PENDING_USER
from authentik.flows.stage import ChallengeStageView 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 from authentik.stages.authenticator_webauthn.utils import get_origin, get_rp_id
LOGGER = get_logger() LOGGER = get_logger()
@ -83,7 +82,7 @@ class AuthenticatorWebAuthnStageView(ChallengeStageView):
def get_challenge(self, *args, **kwargs) -> Challenge: def get_challenge(self, *args, **kwargs) -> Challenge:
# clear session variables prior to starting a new registration # clear session variables prior to starting a new registration
self.request.session.pop("challenge", None) self.request.session.pop("challenge", None)
stage: AuthenticateWebAuthnStage = self.executor.current_stage
user = self.get_pending_user() user = self.get_pending_user()
registration_options: PublicKeyCredentialCreationOptions = generate_registration_options( registration_options: PublicKeyCredentialCreationOptions = generate_registration_options(
@ -94,7 +93,7 @@ class AuthenticatorWebAuthnStageView(ChallengeStageView):
user_display_name=user.name, user_display_name=user.name,
authenticator_selection=AuthenticatorSelectionCriteria( authenticator_selection=AuthenticatorSelectionCriteria(
resident_key=ResidentKeyRequirement.PREFERRED, resident_key=ResidentKeyRequirement.PREFERRED,
user_verification=UserVerificationRequirement.PREFERRED, user_verification=str(stage.user_verification),
), ),
) )
registration_options.user.id = user.uid registration_options.user.id = user.uid

View file

@ -15395,6 +15395,14 @@ paths:
schema: schema:
type: string type: string
format: uuid format: uuid
- in: query
name: user_verification
schema:
type: string
enum:
- discouraged
- preferred
- required
tags: tags:
- stages - stages
security: security:
@ -19240,6 +19248,8 @@ components:
nullable: true nullable: true
description: Flow used by an authenticated user to configure this Stage. description: Flow used by an authenticated user to configure this Stage.
If empty, user will not be able to configure this stage. If empty, user will not be able to configure this stage.
user_verification:
$ref: '#/components/schemas/UserVerificationEnum'
required: required:
- component - component
- meta_model_name - meta_model_name
@ -19264,6 +19274,8 @@ components:
nullable: true nullable: true
description: Flow used by an authenticated user to configure this Stage. description: Flow used by an authenticated user to configure this Stage.
If empty, user will not be able to configure this stage. If empty, user will not be able to configure this stage.
user_verification:
$ref: '#/components/schemas/UserVerificationEnum'
required: required:
- name - name
AuthenticatedSession: AuthenticatedSession:
@ -26611,6 +26623,8 @@ components:
nullable: true nullable: true
description: Flow used by an authenticated user to configure this Stage. description: Flow used by an authenticated user to configure this Stage.
If empty, user will not be able to configure this stage. If empty, user will not be able to configure this stage.
user_verification:
$ref: '#/components/schemas/UserVerificationEnum'
PatchedAuthenticatorDuoStageRequest: PatchedAuthenticatorDuoStageRequest:
type: object type: object
description: AuthenticatorDuoStage Serializer description: AuthenticatorDuoStage Serializer
@ -31235,6 +31249,12 @@ components:
- pk - pk
- source - source
- user - user
UserVerificationEnum:
enum:
- required
- preferred
- discouraged
type: string
UserWriteStage: UserWriteStage:
type: object type: object
description: UserWriteStage Serializer description: UserWriteStage Serializer

View file

@ -4325,6 +4325,7 @@ msgstr "Stage(s)"
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts #: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts #: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts #: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
#: src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
#: src/pages/stages/captcha/CaptchaStageForm.ts #: src/pages/stages/captcha/CaptchaStageForm.ts
#: src/pages/stages/consent/ConsentStageForm.ts #: src/pages/stages/consent/ConsentStageForm.ts
#: src/pages/stages/email/EmailStageForm.ts #: src/pages/stages/email/EmailStageForm.ts
@ -5510,6 +5511,22 @@ msgstr "User password writeback"
msgid "User status" msgid "User status"
msgstr "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 #: src/pages/events/utils.ts
msgid "User was written to" msgid "User was written to"
msgstr "User was written to" msgstr "User was written to"

View file

@ -4285,6 +4285,7 @@ msgstr "Étape(s)"
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts #: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts #: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts #: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
#: src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
#: src/pages/stages/captcha/CaptchaStageForm.ts #: src/pages/stages/captcha/CaptchaStageForm.ts
#: src/pages/stages/consent/ConsentStageForm.ts #: src/pages/stages/consent/ConsentStageForm.ts
#: src/pages/stages/email/EmailStageForm.ts #: src/pages/stages/email/EmailStageForm.ts
@ -5448,6 +5449,22 @@ msgstr "Réécriture du mot de passe utilisateur"
msgid "User status" msgid "User status"
msgstr "Statut utilisateur" 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 #: src/pages/events/utils.ts
msgid "User was written to" msgid "User was written to"
msgstr "L'utilisateur a été écrit vers " msgstr "L'utilisateur a été écrit vers "

View file

@ -4315,6 +4315,7 @@ msgstr ""
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts #: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts #: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts #: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
#: src/pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
#: src/pages/stages/captcha/CaptchaStageForm.ts #: src/pages/stages/captcha/CaptchaStageForm.ts
#: src/pages/stages/consent/ConsentStageForm.ts #: src/pages/stages/consent/ConsentStageForm.ts
#: src/pages/stages/email/EmailStageForm.ts #: src/pages/stages/email/EmailStageForm.ts
@ -5490,6 +5491,22 @@ msgstr ""
msgid "User status" msgid "User status"
msgstr "" 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 #: src/pages/events/utils.ts
msgid "User was written to" msgid "User was written to"
msgstr "" msgstr ""

View file

@ -1,3 +1,5 @@
import { UserVerificationEnum } from "@goauthentik/api/dist/models/UserVerificationEnum";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
@ -52,6 +54,40 @@ export class AuthenticateWebAuthnStageForm extends ModelForm<AuthenticateWebAuth
required required
/> />
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-group .expanded=${true}>
<span slot="header"> ${t`Stage-specific settings`} </span>
<div slot="body" class="pf-c-form">
<ak-form-element-horizontal
label=${t`User verification`}
?required=${true}
name="userVerification"
>
<select name="users" class="pf-c-form-control">
<option
value="${UserVerificationEnum.Required}"
?selected=${this.instance?.userVerification ===
UserVerificationEnum.Required}
>
${t`User verification must occur.`}
</option>
<option
value="${UserVerificationEnum.Preferred}"
?selected=${this.instance?.userVerification ===
UserVerificationEnum.Preferred}
>
${t`User verification is preferred if available, but not required.`}
</option>
<option
value="${UserVerificationEnum.Discouraged}"
?selected=${this.instance?.userVerification ===
UserVerificationEnum.Discouraged}
>
${t`User verification should not occur.`}
</option>
</select>
</ak-form-element-horizontal>
</div>
</ak-form-group>
</form>`; </form>`;
} }
} }