From 82744c5d527015ec9c90605ca475dcfed4e97c70 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Wed, 22 Apr 2026 14:17:39 -0400 Subject: [PATCH] Simplify webauthn code in httpapi Co-authored-by: Copilot --- confluent_server/confluent/httpapi.py | 9 ++------ confluent_server/confluent/webauthn.py | 30 +++++++++++++++----------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/confluent_server/confluent/httpapi.py b/confluent_server/confluent/httpapi.py index a90cfefe..44804553 100644 --- a/confluent_server/confluent/httpapi.py +++ b/confluent_server/confluent/httpapi.py @@ -1012,13 +1012,8 @@ async def resourcehandler_backend(req, make_response): if not webauthn: rsp = await make_response('text/plain', 501, 'Not Implemented') return rsp - try: - wauthbody = webauthn.handle_api_request(url, req, authorized['username'], cfgmgr, reqbody, authorized) - rsp = await make_response() - await rsp.write(wauthbody.encode('utf8')) - except Exception as e: - rsp = await make_response('text/plain', 408) - return rsp + wauthbody = webauthn.handle_api_request(url, req, authorized['username'], cfgmgr, reqbody, authorized) + return await make_response(body=wauthbody) resource = '.' + url[url.rindex('/'):] lquerydict = copy.deepcopy(querydict) try: diff --git a/confluent_server/confluent/webauthn.py b/confluent_server/confluent/webauthn.py index 33456e80..195fbfc0 100644 --- a/confluent_server/confluent/webauthn.py +++ b/confluent_server/confluent/webauthn.py @@ -19,7 +19,7 @@ from webauthn import verify_registration_response from webauthn import verify_authentication_response -challenges = {} +challenges = set([]) CONFIG_MANAGER = None @@ -68,13 +68,6 @@ class User(): pubkey = base64.b64encode(self.credentials.credential_public_key).decode() return {"id": credid, "signature_count": self.credentials.signature_count, "credential_public_key": pubkey} - - def __parse_challenges(self): - if self.challenges: - request = base64.b64encode(self.challenges.request).decode() - return {"id": self.challenges.id, 'request': request} - - @staticmethod def seek_credential_by_id(credential_id): """ @@ -155,7 +148,8 @@ class User(): def save(self): authenticators = CONFIG_MANAGER.get_user(self.username).get('authenticators', {}) authenticators = _load_authenticators(authenticators) - authenticators['challenges'] = self.__parse_challenges() # Looks like the bigger the array we encounter problems changing to just save one challenge + # Let's not retain the transient challenges + #authenticators['challenges'] = self.__parse_challenges() # Looks like the bigger the array we encounter problems changing to just save one challenge authenticators['credentials'] = self.__parse_credentials() CONFIG_MANAGER.set_user(self.username, {'authenticators': authenticators}) @@ -226,9 +220,18 @@ def registration_response(request, username, APP_RELYING_PARTY, APP_ORIGIN): def authentication_request(username, APP_RELYING_PARTY): - user_model = User.get(username) - if not user_model: - raise Exception("Invalid Username") + if username: # WebUI has supplied username and hit enter, only suggest webauthn if we have webauthn registered + user_model = User.get(username) + if not user_model: + raise Exception("Invalid Username") + credential_model = User.get_credential(credential_id=None, username=username) + if not credential_model: + raise Exception("No credential for user") + else: + raise Exception("TODO: Authenticator driven identification") # in theory, we want to support click to authentication without even a username + # but have to sort out matching challenges between requests + + options = generate_authentication_options( rp_id=APP_RELYING_PARTY.id, @@ -292,7 +295,8 @@ def handle_api_request(url, req, username, cfm, reqbody, authorized): cfm.create_user(username, role='Stub') userinfo = cfm.get_user(username) authid = userinfo.get('webauthid', None) - if not authid: + if not authid: # TODO: index users by authid as well as name + # this would entail checking authid for uniqueness as a key once that key structure starts being built authid = secrets.token_bytes(64) b64authid = base64.b64encode(authid).decode() cfm.set_user(username, {'webauthid': b64authid})