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.
request.session["challenge"] = challenge.rstrip("=")
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,
)
assertion = {}
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:

View file

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

View file

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

View file

@ -3370,6 +3370,10 @@ msgstr "Request token URL"
msgid "Required"
msgstr "Required"
#: src/flows/stages/prompt/PromptStage.ts
msgid "Required."
msgstr "Required."
#: src/pages/user-settings/UserSelfForm.ts
#: src/pages/users/ServiceAccountForm.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"
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
msgid "Select an enrollment flow"
msgstr "Select an enrollment flow"
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts
msgid "Select an identification method."
msgstr "Select an identification method."
#:
#~ msgid "Select an identification method."
#~ msgstr "Select an identification method."
#: src/pages/users/GroupSelectModal.ts
msgid "Select groups to add user to"
@ -3757,9 +3765,13 @@ msgstr "Source(s)"
msgid "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
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"
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, or can be used by users to authenticate and enroll themselves."
#: src/pages/flows/BoundStagesList.ts
#: src/pages/flows/StageBindingForm.ts

View file

@ -3362,6 +3362,10 @@ msgstr ""
msgid "Required"
msgstr ""
#: src/flows/stages/prompt/PromptStage.ts
msgid "Required."
msgstr ""
#: src/pages/user-settings/UserSelfForm.ts
#: src/pages/users/ServiceAccountForm.ts
#: src/pages/users/UserForm.ts
@ -3516,13 +3520,17 @@ msgstr ""
msgid "Select all rows"
msgstr ""
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts
msgid "Select an authentication method."
msgstr ""
#: src/pages/stages/invitation/InvitationListLink.ts
msgid "Select an enrollment flow"
msgstr ""
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts
msgid "Select an identification method."
msgstr ""
#:
#~ msgid "Select an identification method."
#~ msgstr ""
#: src/pages/users/GroupSelectModal.ts
msgid "Select groups to add user to"
@ -3749,8 +3757,12 @@ msgstr ""
msgid "Sources"
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
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 ""
#: src/pages/flows/BoundStagesList.ts