Skip to content

Commit 661d3b1

Browse files
refactor(webauthn): stamp transport on the response instead of threading it
1 parent d834534 commit 661d3b1

13 files changed

Lines changed: 43 additions & 62 deletions

File tree

libwebauthn/examples/ceremony/webauthn_ble.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
7575
.unwrap();
7676
println!("WebAuthn MakeCredential response: {:?}", response);
7777

78-
match response.to_json_string(
79-
&make_credentials_request,
80-
channel.transport(),
81-
JsonFormat::Prettified,
82-
) {
78+
match response.to_json_string(&make_credentials_request, JsonFormat::Prettified) {
8379
Ok(response_json) => {
8480
println!(
8581
"WebAuthn MakeCredential response (JSON):\n{}",
@@ -117,11 +113,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
117113
println!("WebAuthn GetAssertion response: {:?}", response);
118114

119115
for assertion in &response.assertions {
120-
match assertion.to_json_string(
121-
&get_assertion,
122-
channel.transport(),
123-
JsonFormat::Prettified,
124-
) {
116+
match assertion.to_json_string(&get_assertion, JsonFormat::Prettified) {
125117
Ok(assertion_json) => {
126118
println!("WebAuthn GetAssertion response (JSON):\n{}", assertion_json);
127119
}

libwebauthn/examples/ceremony/webauthn_cable.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
9292

9393
let response = retry_user_errors!(channel.webauthn_make_credential(&request)).unwrap();
9494
let response_json = response
95-
.to_json_string(&request, channel.transport(), JsonFormat::Prettified)
95+
.to_json_string(&request, JsonFormat::Prettified)
9696
.expect("Failed to serialize MakeCredential response");
9797
println!("WebAuthn MakeCredential response (JSON):\n{response_json}");
9898

libwebauthn/examples/ceremony/webauthn_cable_wss.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
110110

111111
let response = retry_user_errors!(channel.webauthn_make_credential(&request)).unwrap();
112112
let response_json = response
113-
.to_json_string(&request, channel.transport(), JsonFormat::Prettified)
113+
.to_json_string(&request, JsonFormat::Prettified)
114114
.expect("Failed to serialize MakeCredential response");
115115
println!("WebAuthn MakeCredential response (JSON):\n{response_json}");
116116
}
@@ -182,7 +182,7 @@ async fn run_get_assertion(
182182
let response = retry_user_errors!(channel.webauthn_get_assertion(&request)).unwrap();
183183
for assertion in &response.assertions {
184184
let assertion_json = assertion
185-
.to_json_string(&request, channel.transport(), JsonFormat::Prettified)
185+
.to_json_string(&request, JsonFormat::Prettified)
186186
.expect("Failed to serialize GetAssertion response");
187187
println!("WebAuthn GetAssertion response (JSON):\n{assertion_json}");
188188
}

libwebauthn/examples/ceremony/webauthn_hid.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
7979
.unwrap();
8080
println!("WebAuthn MakeCredential response: {:?}", response);
8181

82-
match response.to_json_string(
83-
&make_credentials_request,
84-
channel.transport(),
85-
JsonFormat::Prettified,
86-
) {
82+
match response.to_json_string(&make_credentials_request, JsonFormat::Prettified) {
8783
Ok(response_json) => {
8884
println!(
8985
"WebAuthn MakeCredential response (JSON):\n{}",
@@ -121,11 +117,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
121117
println!("WebAuthn GetAssertion response: {:?}", response);
122118

123119
for assertion in &response.assertions {
124-
match assertion.to_json_string(
125-
&get_assertion,
126-
channel.transport(),
127-
JsonFormat::Prettified,
128-
) {
120+
match assertion.to_json_string(&get_assertion, JsonFormat::Prettified) {
129121
Ok(assertion_json) => {
130122
println!("WebAuthn GetAssertion response (JSON):\n{}", assertion_json);
131123
}

libwebauthn/examples/ceremony/webauthn_nfc.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
7777
let response =
7878
retry_user_errors!(channel.webauthn_make_credential(&make_credentials_request)).unwrap();
7979
let response_json = response
80-
.to_json_string(
81-
&make_credentials_request,
82-
channel.transport(),
83-
JsonFormat::Prettified,
84-
)
80+
.to_json_string(&make_credentials_request, JsonFormat::Prettified)
8581
.expect("Failed to serialize MakeCredential response");
8682
println!("WebAuthn MakeCredential response (JSON):\n{response_json}");
8783

@@ -104,7 +100,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
104100
let response = retry_user_errors!(channel.webauthn_get_assertion(&get_assertion)).unwrap();
105101
for assertion in &response.assertions {
106102
let assertion_json = assertion
107-
.to_json_string(&get_assertion, channel.transport(), JsonFormat::Prettified)
103+
.to_json_string(&get_assertion, JsonFormat::Prettified)
108104
.expect("Failed to serialize GetAssertion response");
109105
println!("WebAuthn GetAssertion response (JSON):\n{assertion_json}");
110106
}

libwebauthn/examples/features/webauthn_prf_cable.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ async fn create() -> Result<(), Box<dyn Error>> {
124124
let response = retry_user_errors!(channel.webauthn_make_credential(&request)).unwrap();
125125

126126
let response_json = response
127-
.to_json_string(&request, channel.transport(), JsonFormat::Prettified)
127+
.to_json_string(&request, JsonFormat::Prettified)
128128
.expect("Failed to serialize MakeCredential response");
129129
println!("WebAuthn MakeCredential response (JSON):\n{response_json}");
130130

@@ -191,7 +191,7 @@ async fn get(credential_id: Option<&str>) -> Result<(), Box<dyn Error>> {
191191

192192
for (num, assertion) in response.assertions.iter().enumerate() {
193193
let assertion_json = assertion
194-
.to_json_string(&request, channel.transport(), JsonFormat::Prettified)
194+
.to_json_string(&request, JsonFormat::Prettified)
195195
.expect("Failed to serialize GetAssertion response");
196196
println!("Assertion {num} (JSON):\n{assertion_json}");
197197
}

libwebauthn/examples/features/webauthn_related_origins_hid.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
7676

7777
let response = retry_user_errors!(channel.webauthn_make_credential(&request)).unwrap();
7878
let response_json = response
79-
.to_json_string(&request, channel.transport(), JsonFormat::Prettified)
79+
.to_json_string(&request, JsonFormat::Prettified)
8080
.expect("Failed to serialize MakeCredential response");
8181
println!("WebAuthn MakeCredential response (JSON):\n{response_json}");
8282
}

libwebauthn/src/ops/webauthn/get_assertion.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,6 @@ impl WebAuthnIDLResponse for Assertion {
489489
fn to_idl_model(
490490
&self,
491491
request: &Self::Context,
492-
_transport: Option<crate::Transport>,
493492
) -> Result<Self::IdlModel, ResponseSerializationError> {
494493
// Get credential ID - either from credential_id field or from authenticator_data
495494
let credential_id_bytes = self
@@ -1307,7 +1306,7 @@ mod tests {
13071306

13081307
let assertion = create_test_assertion();
13091308
let request = create_test_request();
1310-
let json = assertion.to_json_string(&request, None, JsonFormat::default());
1309+
let json = assertion.to_json_string(&request, JsonFormat::default());
13111310
assert!(json.is_ok());
13121311

13131312
let json_str = json.unwrap();
@@ -1333,7 +1332,7 @@ mod tests {
13331332
fn test_assertion_to_idl_model() {
13341333
let assertion = create_test_assertion();
13351334
let request = create_test_request();
1336-
let model = assertion.to_idl_model(&request, None).unwrap();
1335+
let model = assertion.to_idl_model(&request).unwrap();
13371336

13381337
// Verify the credential ID
13391338
assert_eq!(model.raw_id.0, vec![0x01, 0x02, 0x03, 0x04]);
@@ -1355,7 +1354,7 @@ mod tests {
13551354
));
13561355

13571356
let request = create_test_request();
1358-
let model = assertion.to_idl_model(&request, None).unwrap();
1357+
let model = assertion.to_idl_model(&request).unwrap();
13591358

13601359
// Verify user handle is present
13611360
assert!(model.response.user_handle.is_some());
@@ -1382,7 +1381,7 @@ mod tests {
13821381
});
13831382

13841383
let request = create_test_request();
1385-
let model = assertion.to_idl_model(&request, None).unwrap();
1384+
let model = assertion.to_idl_model(&request).unwrap();
13861385

13871386
// Verify extension outputs - PRF should be set with correct values
13881387
let prf = model.client_extension_results.prf.as_ref().unwrap();
@@ -1404,7 +1403,7 @@ mod tests {
14041403
});
14051404

14061405
let request = create_test_request();
1407-
let model = assertion.to_idl_model(&request, None).unwrap();
1406+
let model = assertion.to_idl_model(&request).unwrap();
14081407
assert_eq!(model.client_extension_results.appid, Some(true));
14091408

14101409
// The output should also round-trip through the JSON wire format.
@@ -1428,7 +1427,7 @@ mod tests {
14281427
});
14291428

14301429
let request = create_test_request();
1431-
let model = assertion.to_idl_model(&request, None).unwrap();
1430+
let model = assertion.to_idl_model(&request).unwrap();
14321431
assert_eq!(model.client_extension_results.appid, None);
14331432

14341433
let json = serde_json::to_value(&model.client_extension_results).unwrap();

libwebauthn/src/ops/webauthn/idl/response.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,33 +55,28 @@ pub trait WebAuthnIDLResponse: Sized {
5555
/// Context required for serialization (e.g., client data JSON).
5656
type Context;
5757

58-
/// Converts this response to a JSON-serializable IDL model. `transport` is the
59-
/// transport the ceremony ran over, used to populate the registration
60-
/// `transports` member. Pass `None` when it is unknown.
58+
/// Converts this response to a JSON-serializable IDL model.
6159
fn to_idl_model(
6260
&self,
6361
ctx: &Self::Context,
64-
transport: Option<crate::Transport>,
6562
) -> Result<Self::IdlModel, ResponseSerializationError>;
6663

6764
/// Serializes this response to a `serde_json::Value`.
6865
fn to_json_value(
6966
&self,
7067
ctx: &Self::Context,
71-
transport: Option<crate::Transport>,
7268
) -> Result<serde_json::Value, ResponseSerializationError> {
73-
let model = self.to_idl_model(ctx, transport)?;
69+
let model = self.to_idl_model(ctx)?;
7470
Ok(serde_json::to_value(&model)?)
7571
}
7672

7773
/// Serializes this response to a JSON string.
7874
fn to_json_string(
7975
&self,
8076
ctx: &Self::Context,
81-
transport: Option<crate::Transport>,
8277
format: JsonFormat,
8378
) -> Result<String, ResponseSerializationError> {
84-
let value = self.to_json_value(ctx, transport)?;
79+
let value = self.to_json_value(ctx)?;
8580
match format {
8681
JsonFormat::Minified => Ok(serde_json::to_string(&value)?),
8782
JsonFormat::Prettified => Ok(serde_json::to_string_pretty(&value)?),

libwebauthn/src/ops/webauthn/make_credential.rs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ pub struct MakeCredentialResponse {
4747
pub enterprise_attestation: Option<bool>,
4848
pub large_blob_key: Option<Vec<u8>>,
4949
pub unsigned_extensions_output: MakeCredentialsResponseUnsignedExtensions,
50+
/// Transport the credential was created over, stamped by the channel.
51+
pub transport: Option<crate::Transport>,
5052
}
5153

5254
/// Serializable attestation object for CBOR encoding.
@@ -82,7 +84,6 @@ impl WebAuthnIDLResponse for MakeCredentialResponse {
8284
fn to_idl_model(
8385
&self,
8486
request: &Self::Context,
85-
transport: Option<crate::Transport>,
8687
) -> Result<Self::IdlModel, ResponseSerializationError> {
8788
// The AT flag MUST be set on makeCredential responses per CTAP 2.2 §6.1.
8889
let attested = self
@@ -118,7 +119,7 @@ impl WebAuthnIDLResponse for MakeCredentialResponse {
118119
// Build attestation object (CBOR map with authData, fmt, attStmt)
119120
let attestation_object_bytes = self.build_attestation_object(&authenticator_data_bytes)?;
120121

121-
let transports = registration_transports(transport);
122+
let transports = registration_transports(self.transport);
122123

123124
// Build client extension results
124125
let client_extension_results = self.build_client_extension_results();
@@ -1424,6 +1425,7 @@ mod tests {
14241425
enterprise_attestation: None,
14251426
large_blob_key: None,
14261427
unsigned_extensions_output: MakeCredentialsResponseUnsignedExtensions::default(),
1428+
transport: None,
14271429
}
14281430
}
14291431

@@ -1449,7 +1451,7 @@ mod tests {
14491451

14501452
let response = create_test_response();
14511453
let request = create_test_request();
1452-
let json = response.to_json_string(&request, None, JsonFormat::default());
1454+
let json = response.to_json_string(&request, JsonFormat::default());
14531455
assert!(json.is_ok());
14541456

14551457
let json_str = json.unwrap();
@@ -1504,7 +1506,7 @@ mod tests {
15041506
fn test_response_to_idl_model() {
15051507
let response = create_test_response();
15061508
let request = create_test_request();
1507-
let model = response.to_idl_model(&request, None).unwrap();
1509+
let model = response.to_idl_model(&request).unwrap();
15081510

15091511
// Verify the credential ID
15101512
assert_eq!(model.raw_id.0, vec![0x01, 0x02, 0x03, 0x04]);
@@ -1523,7 +1525,7 @@ mod tests {
15231525
// WebAuthn L3 §5.2.1.1: the registration `transports` member reports the
15241526
// transport the credential was created over, as AuthenticatorTransport tokens.
15251527
// Both the FIDO2 and U2F-downgrade paths converge on this serialization.
1526-
let response = create_test_response();
1528+
let mut response = create_test_response();
15271529
let request = create_test_request();
15281530

15291531
for (transport, token) in [
@@ -1532,15 +1534,16 @@ mod tests {
15321534
(crate::Transport::Nfc, "nfc"),
15331535
(crate::Transport::Hybrid, "hybrid"),
15341536
] {
1535-
let model = response.to_idl_model(&request, Some(transport)).unwrap();
1537+
response.transport = Some(transport);
1538+
let model = response.to_idl_model(&request).unwrap();
15361539
assert_eq!(model.response.transports, vec![token.to_string()]);
15371540
}
15381541

15391542
// The token reaches the JSON wire format too.
1543+
response.transport = Some(crate::Transport::Nfc);
15401544
let json = response
15411545
.to_json_string(
15421546
&request,
1543-
Some(crate::Transport::Nfc),
15441547
crate::ops::webauthn::idl::response::JsonFormat::default(),
15451548
)
15461549
.unwrap();
@@ -1549,7 +1552,8 @@ mod tests {
15491552
assert_eq!(transports, &vec![serde_json::Value::from("nfc")]);
15501553

15511554
// An unknown transport leaves the list empty.
1552-
let model = response.to_idl_model(&request, None).unwrap();
1555+
response.transport = None;
1556+
let model = response.to_idl_model(&request).unwrap();
15531557
assert!(model.response.transports.is_empty());
15541558
}
15551559

@@ -1562,7 +1566,7 @@ mod tests {
15621566
// by the secp256r1 OID and the uncompressed point.
15631567
let response = create_test_response();
15641568
let request = create_test_request();
1565-
let model = response.to_idl_model(&request, None).unwrap();
1569+
let model = response.to_idl_model(&request).unwrap();
15661570

15671571
let public_key_bytes = model
15681572
.response
@@ -1586,7 +1590,7 @@ mod tests {
15861590
fn test_response_attestation_object_format() {
15871591
let response = create_test_response();
15881592
let request = create_test_request();
1589-
let model = response.to_idl_model(&request, None).unwrap();
1593+
let model = response.to_idl_model(&request).unwrap();
15901594

15911595
// Decode the attestation object
15921596
let attestation_bytes = model.response.attestation_object.0;
@@ -1631,7 +1635,7 @@ mod tests {
16311635
};
16321636

16331637
let request = create_test_request();
1634-
let model = response.to_idl_model(&request, None).unwrap();
1638+
let model = response.to_idl_model(&request).unwrap();
16351639

16361640
// Verify cred_props extension
16371641
let cred_props = model.client_extension_results.cred_props.as_ref().unwrap();

0 commit comments

Comments
 (0)