From a6692c2ebf209d9200062739084e040dc0d2e75a Mon Sep 17 00:00:00 2001 From: Max Ustinov Date: Fri, 16 Oct 2026 01:26:33 -0700 Subject: [PATCH] fix(yubikey): fall back to the RP ID origin when the server sends no appid yubikey_authenticate took the WebAuthn client origin only from the appid extension. keeperapp omits appid when its host is local (localhost, as on a default Self-Managed Keeper install), so Commander signed an empty origin and 2fa_validate rejected the assertion with two_factor_code_invalid. Use https:// instead, as the biometric client already does. The legacy U2F retry still uses appid as before. --- keepercommander/yubikey/yubikey.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/keepercommander/yubikey/yubikey.py b/keepercommander/yubikey/yubikey.py index 994c7e36d..b9c65ac7f 100644 --- a/keepercommander/yubikey/yubikey.py +++ b/keepercommander/yubikey/yubikey.py @@ -140,10 +140,10 @@ def yubikey_authenticate(request): # type: (dict) -> Optional[AuthenticationRes return options = request['publicKeyCredentialRequestOptions'].copy() - origin = '' + appid = '' if 'extensions' in options: extensions = options['extensions'] - origin = extensions.get('appid') or '' + appid = extensions.get('appid') or '' if 'largeBlob' not in options['extensions']: options['extensions']['largeBlob'] = {'read': None} @@ -156,6 +156,9 @@ def yubikey_authenticate(request): # type: (dict) -> Optional[AuthenticationRes if isinstance(challenge, str): options['challenge'] = utils.base64_url_decode(challenge) + # The server omits the appid extension when its host is local; the origin is then the RP ID's. + origin = appid or (f'https://{options["rpId"]}' if options.get('rpId') else '') + client = None # type: Optional[WebAuthnClient] data_collector = DefaultClientDataCollector(origin, verify=verify_rp_id_none) if os.name == 'nt': @@ -195,7 +198,7 @@ def yubikey_authenticate(request): # type: (dict) -> Optional[AuthenticationRes 'https://docs.keeper.io/enterprise-guide/two-factor-authentication#security-keys-fido-webauthn\n' 'Commander will use the fallback security key authentication method.\n\n' 'To use your Yubikey with Commander, please touch the flashing Security key one more time.\n') - options['rpId'] = origin + options['rpId'] = appid rq_options = PublicKeyCredentialRequestOptions.from_dict(options) rs = client.get_assertion(rq_options, event=evt) return rs.get_response(0)