stages/authenticator_validate: show single button for multiple webauthn authenticators

tested with browser + yubikey 5

closes #1096

The order of allowCredentials doesn't seem to matter, chrome seems to always choose the internal authenticator first.

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-09-01 19:28:52 +02:00
parent 7d26ea1a9c
commit 4fc8e61f8c
5 changed files with 58 additions and 27 deletions

View File

@ -51,20 +51,29 @@ def get_webauthn_challenge(request: HttpRequest, device: WebAuthnDevice) -> dict
# for the reasons outlined in the comment in webauthn_begin_activate. # for the reasons outlined in the comment in webauthn_begin_activate.
request.session["challenge"] = challenge.rstrip("=") request.session["challenge"] = challenge.rstrip("=")
webauthn_user = WebAuthnUser( assertion = {}
device.user.uid,
device.user.username,
device.user.name,
device.user.avatar,
device.credential_id,
device.public_key,
device.sign_count,
device.rp_id,
)
webauthn_assertion_options = WebAuthnAssertionOptions(webauthn_user, challenge) # We want all the user's WebAuthn devices and merge their challenges
for device in WebAuthnDevice.objects.filter(user=device.user).order_by("name"):
webauthn_user = WebAuthnUser(
device.user.uid,
device.user.username,
device.user.name,
device.user.avatar,
device.credential_id,
device.public_key,
device.sign_count,
device.rp_id,
)
webauthn_assertion_options = WebAuthnAssertionOptions(webauthn_user, challenge)
if assertion == {}:
assertion = webauthn_assertion_options.assertion_dict
else:
assertion["allowCredentials"] += webauthn_assertion_options.assertion_dict.get(
"allowCredentials"
)
return webauthn_assertion_options.assertion_dict return assertion
def validate_challenge_code(code: str, request: HttpRequest, user: User) -> str: def validate_challenge_code(code: str, request: HttpRequest, user: User) -> str:

View File

@ -20,8 +20,6 @@ from authentik.stages.authenticator_validate.models import AuthenticatorValidate
LOGGER = get_logger() LOGGER = get_logger()
PER_DEVICE_CLASSES = [DeviceClasses.WEBAUTHN]
class AuthenticatorValidationChallenge(WithUserInfoChallenge): class AuthenticatorValidationChallenge(WithUserInfoChallenge):
"""Authenticator challenge""" """Authenticator challenge"""
@ -91,9 +89,9 @@ class AuthenticatorValidateStageView(ChallengeStageView):
if device_class not in stage.device_classes: if device_class not in stage.device_classes:
LOGGER.debug("device class not allowed", device_class=device_class) LOGGER.debug("device class not allowed", device_class=device_class)
continue continue
# Ensure only classes in PER_DEVICE_CLASSES are returned per device # Ensure only one challenge per device class
# otherwise only return a single challenge # WebAuthn does another device loop to find all webuahtn devices
if device_class in seen_classes and device_class not in PER_DEVICE_CLASSES: if device_class in seen_classes:
continue continue
if device_class not in seen_classes: if device_class not in seen_classes:
seen_classes.append(device_class) seen_classes.append(device_class)

View File

@ -180,7 +180,7 @@ export class AuthenticatorValidateStage
${this.selectedDeviceChallenge ${this.selectedDeviceChallenge
? "" ? ""
: html`<p class="pf-c-login__main-header-desc"> : html`<p class="pf-c-login__main-header-desc">
${t`Select an identification method.`} ${t`Select an authentication method.`}
</p>`} </p>`}
</header> </header>
${this.selectedDeviceChallenge ${this.selectedDeviceChallenge

View File

@ -3370,6 +3370,10 @@ msgstr "Request token URL"
msgid "Required" msgid "Required"
msgstr "Required" msgstr "Required"
#: src/flows/stages/prompt/PromptStage.ts
msgid "Required."
msgstr "Required."
#: src/pages/user-settings/UserSelfForm.ts #: src/pages/user-settings/UserSelfForm.ts
#: src/pages/users/ServiceAccountForm.ts #: src/pages/users/ServiceAccountForm.ts
#: src/pages/users/UserForm.ts #: src/pages/users/UserForm.ts
@ -3524,13 +3528,17 @@ msgstr "Select a provider that this application should use. Alternatively, creat
msgid "Select all rows" msgid "Select all rows"
msgstr "Select all rows" msgstr "Select all rows"
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts
msgid "Select an authentication method."
msgstr "Select an authentication method."
#: src/pages/stages/invitation/InvitationListLink.ts #: src/pages/stages/invitation/InvitationListLink.ts
msgid "Select an enrollment flow" msgid "Select an enrollment flow"
msgstr "Select an enrollment flow" msgstr "Select an enrollment flow"
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts #:
msgid "Select an identification method." #~ msgid "Select an identification method."
msgstr "Select an identification method." #~ msgstr "Select an identification method."
#: src/pages/users/GroupSelectModal.ts #: src/pages/users/GroupSelectModal.ts
msgid "Select groups to add user to" msgid "Select groups to add user to"
@ -3757,9 +3765,13 @@ msgstr "Source(s)"
msgid "Sources" msgid "Sources"
msgstr "Sources" msgstr "Sources"
#:
#~ msgid "Sources of identities, which can either be synced into authentik's database, like LDAP, or can be used by users to authenticate and enroll themselves, like OAuth and social logins"
#~ msgstr "Sources of identities, which can either be synced into authentik's database, like LDAP, or can be used by users to authenticate and enroll themselves, like OAuth and social logins"
#: src/pages/sources/SourcesListPage.ts #: src/pages/sources/SourcesListPage.ts
msgid "Sources of identities, which can either be synced into authentik's database, like LDAP, or can be used by users to authenticate and enroll themselves, like OAuth and social logins" msgid "Sources of identities, which can either be synced into authentik's database, or can be used by users to authenticate and enroll themselves."
msgstr "Sources of identities, which can either be synced into authentik's database, like LDAP, or can be used by users to authenticate and enroll themselves, like OAuth and social logins" msgstr "Sources of identities, which can either be synced into authentik's database, or can be used by users to authenticate and enroll themselves."
#: src/pages/flows/BoundStagesList.ts #: src/pages/flows/BoundStagesList.ts
#: src/pages/flows/StageBindingForm.ts #: src/pages/flows/StageBindingForm.ts

View File

@ -3362,6 +3362,10 @@ msgstr ""
msgid "Required" msgid "Required"
msgstr "" msgstr ""
#: src/flows/stages/prompt/PromptStage.ts
msgid "Required."
msgstr ""
#: src/pages/user-settings/UserSelfForm.ts #: src/pages/user-settings/UserSelfForm.ts
#: src/pages/users/ServiceAccountForm.ts #: src/pages/users/ServiceAccountForm.ts
#: src/pages/users/UserForm.ts #: src/pages/users/UserForm.ts
@ -3516,13 +3520,17 @@ msgstr ""
msgid "Select all rows" msgid "Select all rows"
msgstr "" msgstr ""
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts
msgid "Select an authentication method."
msgstr ""
#: src/pages/stages/invitation/InvitationListLink.ts #: src/pages/stages/invitation/InvitationListLink.ts
msgid "Select an enrollment flow" msgid "Select an enrollment flow"
msgstr "" msgstr ""
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts #:
msgid "Select an identification method." #~ msgid "Select an identification method."
msgstr "" #~ msgstr ""
#: src/pages/users/GroupSelectModal.ts #: src/pages/users/GroupSelectModal.ts
msgid "Select groups to add user to" msgid "Select groups to add user to"
@ -3749,8 +3757,12 @@ msgstr ""
msgid "Sources" msgid "Sources"
msgstr "" msgstr ""
#:
#~ msgid "Sources of identities, which can either be synced into authentik's database, like LDAP, or can be used by users to authenticate and enroll themselves, like OAuth and social logins"
#~ msgstr ""
#: src/pages/sources/SourcesListPage.ts #: src/pages/sources/SourcesListPage.ts
msgid "Sources of identities, which can either be synced into authentik's database, like LDAP, or can be used by users to authenticate and enroll themselves, like OAuth and social logins" msgid "Sources of identities, which can either be synced into authentik's database, or can be used by users to authenticate and enroll themselves."
msgstr "" msgstr ""
#: src/pages/flows/BoundStagesList.ts #: src/pages/flows/BoundStagesList.ts