Skip to content

Commit 3652b6e

Browse files
feat(webauthn): populate registration transports from authenticator getInfo
1 parent 661d3b1 commit 3652b6e

2 files changed

Lines changed: 51 additions & 1 deletion

File tree

libwebauthn/src/ops/webauthn/make_credential.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ pub struct MakeCredentialResponse {
4949
pub unsigned_extensions_output: MakeCredentialsResponseUnsignedExtensions,
5050
/// Transport the credential was created over, stamped by the channel.
5151
pub transport: Option<crate::Transport>,
52+
/// Transports the authenticator advertised in getInfo (0x09), if any.
53+
pub authenticator_transports: Option<Vec<String>>,
5254
}
5355

5456
/// Serializable attestation object for CBOR encoding.
@@ -119,7 +121,17 @@ impl WebAuthnIDLResponse for MakeCredentialResponse {
119121
// Build attestation object (CBOR map with authData, fmt, attStmt)
120122
let attestation_object_bytes = self.build_attestation_object(&authenticator_data_bytes)?;
121123

122-
let transports = registration_transports(self.transport);
124+
// Prefer the authenticator's getInfo 0x09 transports; else the ceremony
125+
// transport. WebAuthn L3 §5.2.1.1: unique tokens, lexicographically sorted.
126+
let transports = match self.authenticator_transports.as_ref() {
127+
Some(reported) if !reported.is_empty() => {
128+
let mut tokens = reported.clone();
129+
tokens.sort();
130+
tokens.dedup();
131+
tokens
132+
}
133+
_ => registration_transports(self.transport),
134+
};
123135

124136
// Build client extension results
125137
let client_extension_results = self.build_client_extension_results();
@@ -1426,6 +1438,7 @@ mod tests {
14261438
large_blob_key: None,
14271439
unsigned_extensions_output: MakeCredentialsResponseUnsignedExtensions::default(),
14281440
transport: None,
1441+
authenticator_transports: None,
14291442
}
14301443
}
14311444

@@ -1557,6 +1570,42 @@ mod tests {
15571570
assert!(model.response.transports.is_empty());
15581571
}
15591572

1573+
#[test]
1574+
fn test_response_to_idl_model_transports_from_get_info() {
1575+
// The authenticator's getInfo (0x09) transports source the registration
1576+
// `transports` member as unique tokens in lexicographical order, taking
1577+
// precedence over the single ceremony transport (no union).
1578+
let mut response = create_test_response();
1579+
let request = create_test_request();
1580+
1581+
// Reported out of order with a duplicate; ceremony transport differs.
1582+
response.transport = Some(crate::Transport::Ble);
1583+
response.authenticator_transports = Some(vec![
1584+
"usb".to_string(),
1585+
"nfc".to_string(),
1586+
"usb".to_string(),
1587+
]);
1588+
let model = response.to_idl_model(&request).unwrap();
1589+
assert_eq!(
1590+
model.response.transports,
1591+
vec!["nfc".to_string(), "usb".to_string()]
1592+
);
1593+
1594+
// An empty reported list falls back to the ceremony transport.
1595+
response.authenticator_transports = Some(Vec::new());
1596+
let model = response.to_idl_model(&request).unwrap();
1597+
assert_eq!(model.response.transports, vec!["ble".to_string()]);
1598+
1599+
// Unknown tokens pass through unchanged.
1600+
response.authenticator_transports =
1601+
Some(vec!["smart-card".to_string(), "custom".to_string()]);
1602+
let model = response.to_idl_model(&request).unwrap();
1603+
assert_eq!(
1604+
model.response.transports,
1605+
vec!["custom".to_string(), "smart-card".to_string()]
1606+
);
1607+
}
1608+
15601609
#[test]
15611610
fn test_response_emits_spki_for_es256() {
15621611
// The test fixture builds an ES256 P-256 credential, so getPublicKey()

libwebauthn/src/proto/ctap2/model/make_credential.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ impl Ctap2MakeCredentialResponse {
405405
large_blob_key: self.large_blob_key.map(|x| x.into_vec()),
406406
unsigned_extensions_output,
407407
transport: None,
408+
authenticator_transports: info.and_then(|i| i.transports.clone()),
408409
}
409410
}
410411
}

0 commit comments

Comments
 (0)