From 1ea9d6d3cc3a001c5c94e0f40501be16cc94f789 Mon Sep 17 00:00:00 2001 From: pd Date: Fri, 5 Sep 2025 17:55:44 -0700 Subject: [PATCH 1/3] feat: remove registration --- Cargo.lock | 1 + Cargo.toml | 1 + src/auth.rs | 9 +-------- src/backup_storage.rs | 1 - src/types/backup_metadata.rs | 28 +++++++++++----------------- 5 files changed, 14 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6803bb8..50ae76c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -780,6 +780,7 @@ dependencies = [ "schemars 0.8.22", "serde", "serde_json", + "serde_plain", "serial_test", "sha2", "strum 0.27.2", diff --git a/Cargo.toml b/Cargo.toml index c8a4c52..7eb3c17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ reqwest = { version = "0.12.22", features = ["json", "rustls-tls"] } schemars = "0.8.21" serde = { version = "1.0.219", features = ["derive"] } serde_json = { workspace = true } +serde_plain = "1.0.2" sha2 = { workspace = true } strum = "0.27.2" strum_macros = "0.27.1" diff --git a/src/auth.rs b/src/auth.rs index 91f18d6..83471a9 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -253,13 +253,7 @@ impl AuthHandler { .finish_passkey_registration(&user_provided_credential, &passkey_state)?; let credential_id = verified_passkey.cred_id().clone(); - let factor = Factor::new_passkey( - verified_passkey, - serde_json::to_value(credential.clone()).map_err(|err| { - tracing::info!(message = "Failed to serialize passkey credential", error = ?err); - ErrorResponse::internal_server_error() - })?, - ); + let factor = Factor::new_passkey(verified_passkey); let factor_to_lookup = FactorToLookup::from_passkey(URL_SAFE_NO_PAD.encode(credential_id)); Ok((factor, factor_to_lookup)) @@ -317,7 +311,6 @@ impl AuthHandler { .filter_map(|factor| { if let FactorKind::Passkey { webauthn_credential, - registration: _, } = &factor.kind { Some(webauthn_credential.into()) diff --git a/src/backup_storage.rs b/src/backup_storage.rs index 14ab0ab..b7fe395 100644 --- a/src/backup_storage.rs +++ b/src/backup_storage.rs @@ -545,7 +545,6 @@ mod tests { id: test_primary_factor_id.clone(), kind: FactorKind::Passkey { webauthn_credential: serde_json::from_value(test_webauthn_credential).unwrap(), - registration: json!({}), }, created_at: DateTime::default(), }], diff --git a/src/types/backup_metadata.rs b/src/types/backup_metadata.rs index 25ff039..bd3da2b 100644 --- a/src/types/backup_metadata.rs +++ b/src/types/backup_metadata.rs @@ -66,8 +66,12 @@ impl Factor { id: self.id.clone(), created_at: self.created_at.timestamp(), kind: match &self.kind { - FactorKind::Passkey { registration, .. } => ExportedFactorKind::Passkey { - registration: registration.clone(), + FactorKind::Passkey { + webauthn_credential, + .. + } => ExportedFactorKind::Passkey { + credential_id: serde_plain::to_string(webauthn_credential.cred_id()) + .unwrap_or_default(), }, FactorKind::OidcAccount { account, @@ -124,12 +128,7 @@ impl Factor { #[allow(clippy::large_enum_variant)] pub enum FactorKind { #[serde(rename_all = "camelCase")] - Passkey { - webauthn_credential: Passkey, - // Registration object presented by the client when signing up. Used by the client to be - // to register the passkey in Turnkey later, not during initial sign up. - registration: serde_json::Value, - }, + Passkey { webauthn_credential: Passkey }, #[serde(rename_all = "camelCase")] OidcAccount { account: OidcAccountKind, @@ -182,12 +181,11 @@ impl PartialEq for FactorKind { impl Factor { #[must_use] - pub fn new_passkey(webauthn_credential: Passkey, registration: serde_json::Value) -> Self { + pub fn new_passkey(webauthn_credential: Passkey) -> Self { Self { id: Uuid::new_v4().to_string(), kind: FactorKind::Passkey { webauthn_credential, - registration, }, created_at: Utc::now(), } @@ -269,12 +267,10 @@ pub struct ExportedFactor { #[serde(rename_all = "SCREAMING_SNAKE_CASE", tag = "kind")] #[allow(clippy::large_enum_variant)] pub enum ExportedFactorKind { + /// For Passkey, we export (using String because of incompatibility with `JsonSchema`): + /// - the credential ID (type: `CredentialID`) as a base64-url-safe-no-pad string #[serde(rename_all = "camelCase")] - Passkey { - // Registration object presented by the client when signing up. Used by the client to be - // to register the passkey in Turnkey later, not during initial sign up. - registration: serde_json::Value, - }, + Passkey { credential_id: String }, #[serde(rename_all = "camelCase")] OidcAccount { account: ExportedOidcAccountKind, @@ -403,11 +399,9 @@ mod tests { let factor_1 = FactorKind::Passkey { webauthn_credential: passkey.clone(), - registration: json!([1]), }; let factor_2 = FactorKind::Passkey { webauthn_credential: passkey, - registration: json!([2]), }; assert_eq!(factor_1, factor_2); From 75bbea1f217aae67bd3bb609dbe0dfbbdd44e3d0 Mon Sep 17 00:00:00 2001 From: pd Date: Fri, 5 Sep 2025 18:10:27 -0700 Subject: [PATCH 2/3] rm feature flag --- Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7eb3c17..02bdc0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,9 +72,7 @@ tracing-subscriber = { version = "0.3.20", default-features = false, features = "json", ] } uuid = { workspace = true } -# TODO/FIXME: Investigate how dangerous this is considering we're going to be tracking used challenges webauthn-rs = { version = "0.5.2", features = [ - "danger-allow-state-serialisation", "conditional-ui", "workaround-google-passkey-specific-issues", ] } From 525a58abe5ba49bac6aa88f3d27c3c0fd5e99b72 Mon Sep 17 00:00:00 2001 From: pd Date: Fri, 5 Sep 2025 18:38:47 -0700 Subject: [PATCH 3/3] fix --- Cargo.toml | 1 + src/webauthn.rs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 02bdc0a..e58c69b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,7 @@ tracing-subscriber = { version = "0.3.20", default-features = false, features = ] } uuid = { workspace = true } webauthn-rs = { version = "0.5.2", features = [ + "danger-allow-state-serialisation", # see webauthn.rs for details "conditional-ui", "workaround-google-passkey-specific-issues", ] } diff --git a/src/webauthn.rs b/src/webauthn.rs index 9163712..c37e9ad 100644 --- a/src/webauthn.rs +++ b/src/webauthn.rs @@ -1,3 +1,10 @@ +//! This module contains specific logic for `WebAuthn` operations (used for Passkey serialization and deserialization). +//! +//! Particularly for the `webauthn-rs` crate we use the `danger-allow-state-serialisation` feature flag. This +//! is required to serialize and deserialize the `PasskeyRegistration` and `PasskeyAuthentication` structs. It +//! is safe because the states are kept server-accessible only (stored in the encrypted Challenge token, see `ChallengeManager`). +//! Reference: + use crate::types::ErrorResponse; use serde_json::Value; use webauthn_rs::prelude::PublicKeyCredential;