From fc1f8d5514698c4b5fce3c50732408763db859d1 Mon Sep 17 00:00:00 2001 From: Kyle Pope Date: Tue, 17 Mar 2026 23:40:26 +0800 Subject: [PATCH] Fix passkey registration: use correct py_webauthn credential parsers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RegistrationCredential and AuthenticationCredential are plain dataclasses, not Pydantic models — model_validate_json() does not exist on them. Replace with parse_registration_credential_json() and parse_authentication_credential_json() from webauthn.helpers, which correctly parse the camelCase JSON from @simplewebauthn/browser and convert base64url fields to bytes. Co-Authored-By: Claude Opus 4.6 (1M context) --- .env.example | 9 +++++++++ backend/app/services/passkey.py | 15 ++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/.env.example b/.env.example index 9ac77b4..9ba0e21 100644 --- a/.env.example +++ b/.env.example @@ -21,6 +21,15 @@ ENVIRONMENT=development # Timezone (applied to backend + db containers via env_file) TZ=Australia/Perth +# ────────────────────────────────────── +# WebAuthn / Passkeys +# ────────────────────────────────────── +# REQUIRED for passkeys to work. Must match the domain users access UMBRA on. +# RP_ID = eTLD+1 (no scheme, no port). ORIGIN = full origin with scheme. +WEBAUTHN_RP_ID=umbra.example.com +WEBAUTHN_RP_NAME=UMBRA +WEBAUTHN_ORIGIN=https://umbra.example.com + # ────────────────────────────────────── # Integrations # ────────────────────────────────────── diff --git a/backend/app/services/passkey.py b/backend/app/services/passkey.py index 307b1c8..dfb0a79 100644 --- a/backend/app/services/passkey.py +++ b/backend/app/services/passkey.py @@ -28,7 +28,12 @@ from webauthn.helpers.structs import ( UserVerificationRequirement, AttestationConveyancePreference, ) -from webauthn.helpers import bytes_to_base64url, base64url_to_bytes +from webauthn.helpers import ( + bytes_to_base64url, + base64url_to_bytes, + parse_registration_credential_json, + parse_authentication_credential_json, +) from app.config import settings as app_settings @@ -148,9 +153,7 @@ def verify_registration( Returns VerifiedRegistration on success, raises on failure. """ - from webauthn.helpers.structs import RegistrationCredential - - credential = RegistrationCredential.model_validate_json(credential_json) + credential = parse_registration_credential_json(credential_json) return verify_registration_response( credential=credential, expected_challenge=challenge, @@ -209,9 +212,7 @@ def verify_authentication( Returns VerifiedAuthentication on success, raises on failure. Sign count anomalies are NOT hard-failed — caller should log and continue. """ - from webauthn.helpers.structs import AuthenticationCredential - - credential = AuthenticationCredential.model_validate_json(credential_json) + credential = parse_authentication_credential_json(credential_json) return verify_authentication_response( credential=credential, expected_challenge=challenge,