ser.rs (36885B)
1 use super::{ 2 super::response::ser::Base64DecodedVal, AsciiDomain, AsciiDomainStatic, Challenge, 3 CredentialId, CredentialMediationRequirement, ExtensionReq, Hint, PrfInput, 4 PublicKeyCredentialDescriptor, RpId, Url, UserVerificationRequirement, auth::PrfInputOwned, 5 }; 6 use core::{ 7 fmt::{self, Formatter}, 8 str::FromStr as _, 9 }; 10 use serde::{ 11 de::{Deserialize, Deserializer, Error, MapAccess, SeqAccess, Unexpected, Visitor}, 12 ser::{Serialize, SerializeSeq as _, SerializeStruct as _, Serializer}, 13 }; 14 /// `"required"`. 15 const REQUIRED: &str = "required"; 16 /// `"conditional"`. 17 const CONDITIONAL: &str = "conditional"; 18 impl Serialize for CredentialMediationRequirement { 19 /// Serializes `self` to conform with 20 /// [`CredentialMediationRequirement`](https://www.w3.org/TR/credential-management-1/#enumdef-credentialmediationrequirement). 21 /// 22 /// # Examples 23 /// 24 /// ``` 25 /// # use webauthn_rp::request::CredentialMediationRequirement; 26 /// assert_eq!( 27 /// serde_json::to_string(&CredentialMediationRequirement::Required)?, 28 /// r#""required""# 29 /// ); 30 /// assert_eq!( 31 /// serde_json::to_string(&CredentialMediationRequirement::Conditional)?, 32 /// r#""conditional""# 33 /// ); 34 /// # Ok::<_, serde_json::Error>(()) 35 /// ``` 36 #[inline] 37 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 38 where 39 S: Serializer, 40 { 41 serializer.serialize_str(match *self { 42 Self::Required => REQUIRED, 43 Self::Conditional => CONDITIONAL, 44 }) 45 } 46 } 47 impl Serialize for Challenge { 48 /// Serializes `self` to conform with 49 /// [`challenge`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptionsjson-challenge). 50 /// 51 /// Specifically [`Self::as_array`] is transformed into a base64url-encoded string. 52 /// 53 /// # Examples 54 /// 55 /// ``` 56 /// # use webauthn_rp::request::Challenge; 57 /// # // `Challenge::BASE64_LEN` is 22, but we add two for the quotes. 58 /// assert_eq!(serde_json::to_string(&Challenge::new())?.len(), 24); 59 /// # Ok::<_, serde_json::Error>(()) 60 /// ``` 61 #[inline] 62 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 63 where 64 S: Serializer, 65 { 66 serializer.serialize_str(base64url_nopad::encode_buffer( 67 self.as_array().as_slice(), 68 [0; Self::BASE64_LEN].as_mut_slice(), 69 )) 70 } 71 } 72 impl Serialize for AsciiDomain { 73 /// Serializes `self` as a [`prim@str`]. 74 /// 75 /// # Examples 76 /// 77 /// ``` 78 /// # use webauthn_rp::request::AsciiDomain; 79 /// assert_eq!( 80 /// serde_json::to_string(&AsciiDomain::try_from("www.example.com".to_owned()).unwrap()).unwrap(), 81 /// r#""www.example.com""# 82 /// ); 83 /// ``` 84 #[inline] 85 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 86 where 87 S: Serializer, 88 { 89 serializer.serialize_str(self.as_ref()) 90 } 91 } 92 impl Serialize for AsciiDomainStatic { 93 /// Serializes `self` as a [`prim@str`]. 94 /// 95 /// # Examples 96 /// 97 /// ``` 98 /// # use webauthn_rp::request::AsciiDomainStatic; 99 /// assert_eq!( 100 /// serde_json::to_string(&AsciiDomainStatic::new("www.example.com").unwrap()).unwrap(), 101 /// r#""www.example.com""# 102 /// ); 103 /// ``` 104 #[inline] 105 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 106 where 107 S: Serializer, 108 { 109 serializer.serialize_str(self.as_str()) 110 } 111 } 112 impl Serialize for Url { 113 /// Serializes `self` as a [`prim@str`]. 114 /// 115 /// # Examples 116 /// 117 /// ``` 118 /// # use core::str::FromStr as _; 119 /// # use webauthn_rp::request::Url; 120 /// assert_eq!( 121 /// serde_json::to_string(&Url::from_str("ssh:foo").unwrap()).unwrap(), 122 /// r#""ssh:foo""# 123 /// ); 124 /// ``` 125 #[inline] 126 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 127 where 128 S: Serializer, 129 { 130 serializer.serialize_str(self.as_ref()) 131 } 132 } 133 impl Serialize for RpId { 134 /// Serializes `self` as a [`prim@str`]. 135 /// 136 /// # Examples 137 /// 138 /// ``` 139 /// # use webauthn_rp::request::{AsciiDomain, RpId}; 140 /// assert_eq!( 141 /// serde_json::to_string(&RpId::Domain(AsciiDomain::try_from("www.example.com".to_owned()).unwrap())).unwrap(), 142 /// r#""www.example.com""# 143 /// ); 144 /// assert_eq!( 145 /// serde_json::to_string(&RpId::Url("ssh:foo".parse().unwrap())).unwrap(), 146 /// r#""ssh:foo""# 147 /// ); 148 /// # Ok::<_, serde_json::Error>(()) 149 /// ``` 150 #[inline] 151 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 152 where 153 S: Serializer, 154 { 155 serializer.serialize_str(self.as_ref()) 156 } 157 } 158 impl<T> Serialize for PublicKeyCredentialDescriptor<T> 159 where 160 CredentialId<T>: Serialize, 161 { 162 /// Serializes `self` to conform with 163 /// [`PublicKeyCredentialDescriptorJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialdescriptorjson). 164 /// 165 /// # Examples 166 /// 167 /// ``` 168 /// # #[cfg(all(feature = "bin", feature = "custom"))] 169 /// # use webauthn_rp::{bin::Decode, response::bin::DecodeAuthTransportsErr}; 170 /// # use webauthn_rp::{ 171 /// # request::PublicKeyCredentialDescriptor, 172 /// # response::{AuthTransports, CredentialId}, 173 /// # }; 174 /// /// Retrieves the `AuthTransports` associated with the unique `cred_id` 175 /// /// from the database. 176 /// # #[cfg(all(feature = "bin", feature = "custom"))] 177 /// fn get_transports(cred_id: CredentialId<&[u8]>) -> Result<AuthTransports, DecodeAuthTransportsErr> { 178 /// // ⋮ 179 /// # AuthTransports::decode(32) 180 /// } 181 /// // `CredentialId::try_from` only exists when `custom` is enabled; and even then, it is 182 /// // likely never needed since the `CredentialId` was originally sent from the client and is likely 183 /// // stored in a database which would be fetched by `UserHandle` or `Authentication::raw_id`. 184 /// # #[cfg(all(feature = "bin", feature = "custom"))] 185 /// let id = CredentialId::try_from(vec![0; 16])?; 186 /// # #[cfg(all(feature = "bin", feature = "custom"))] 187 /// let transports = get_transports((&id).into())?; 188 /// # #[cfg(all(feature = "bin", feature = "custom"))] 189 /// assert_eq!( 190 /// serde_json::to_string(&PublicKeyCredentialDescriptor { id, transports }).unwrap(), 191 /// r#"{"type":"public-key","id":"AAAAAAAAAAAAAAAAAAAAAA","transports":["usb"]}"# 192 /// ); 193 /// # Ok::<_, webauthn_rp::AggErr>(()) 194 /// ``` 195 #[inline] 196 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 197 where 198 S: Serializer, 199 { 200 serializer 201 .serialize_struct("PublicKeyCredentialDescriptor", 3) 202 .and_then(|mut ser| { 203 ser.serialize_field("type", "public-key").and_then(|()| { 204 ser.serialize_field("id", &self.id).and_then(|()| { 205 ser.serialize_field("transports", &self.transports) 206 .and_then(|()| ser.end()) 207 }) 208 }) 209 }) 210 } 211 } 212 impl Serialize for UserVerificationRequirement { 213 /// Serializes `self` to conform with 214 /// [`UserVerificationRequirement`](https://www.w3.org/TR/webauthn-3/#enumdef-userverificationrequirement). 215 /// 216 /// # Examples 217 /// 218 /// ``` 219 /// # use webauthn_rp::request::UserVerificationRequirement; 220 /// assert_eq!( 221 /// serde_json::to_string(&UserVerificationRequirement::Required)?, 222 /// r#""required""# 223 /// ); 224 /// assert_eq!( 225 /// serde_json::to_string(&UserVerificationRequirement::Discouraged)?, 226 /// r#""discouraged""# 227 /// ); 228 /// assert_eq!( 229 /// serde_json::to_string(&UserVerificationRequirement::Preferred)?, 230 /// r#""preferred""# 231 /// ); 232 /// # Ok::<_, serde_json::Error>(()) 233 /// ``` 234 #[inline] 235 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 236 where 237 S: Serializer, 238 { 239 serializer.serialize_str(match *self { 240 Self::Required => "required", 241 Self::Discouraged => "discouraged", 242 Self::Preferred => "preferred", 243 }) 244 } 245 } 246 /// [`security-key`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialhints-security-key). 247 const SECURITY_KEY: &str = "security-key"; 248 /// [`client-device`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialhints-client-device). 249 const CLIENT_DEVICE: &str = "client-device"; 250 /// [`hybrid`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialhints-hybrid). 251 const HYBRID: &str = "hybrid"; 252 impl Serialize for Hint { 253 /// Serializes `self` to conform with 254 /// [`hints`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptionsjson-hints). 255 /// 256 /// # Examples 257 /// 258 /// ``` 259 /// # use webauthn_rp::request::Hint; 260 /// assert_eq!( 261 /// serde_json::to_string(&Hint::None)?, 262 /// r#"[]"# 263 /// ); 264 /// assert_eq!( 265 /// serde_json::to_string(&Hint::SecurityKey)?, 266 /// r#"["security-key"]"# 267 /// ); 268 /// assert_eq!( 269 /// serde_json::to_string(&Hint::ClientDevice)?, 270 /// r#"["client-device"]"# 271 /// ); 272 /// assert_eq!( 273 /// serde_json::to_string(&Hint::Hybrid)?, 274 /// r#"["hybrid"]"# 275 /// ); 276 /// assert_eq!( 277 /// serde_json::to_string(&Hint::SecurityKeyClientDevice)?, 278 /// r#"["security-key","client-device"]"# 279 /// ); 280 /// assert_eq!( 281 /// serde_json::to_string(&Hint::ClientDeviceSecurityKey)?, 282 /// r#"["client-device","security-key"]"# 283 /// ); 284 /// assert_eq!( 285 /// serde_json::to_string(&Hint::SecurityKeyHybrid)?, 286 /// r#"["security-key","hybrid"]"# 287 /// ); 288 /// assert_eq!( 289 /// serde_json::to_string(&Hint::HybridSecurityKey)?, 290 /// r#"["hybrid","security-key"]"# 291 /// ); 292 /// assert_eq!( 293 /// serde_json::to_string(&Hint::ClientDeviceHybrid)?, 294 /// r#"["client-device","hybrid"]"# 295 /// ); 296 /// assert_eq!( 297 /// serde_json::to_string(&Hint::HybridClientDevice)?, 298 /// r#"["hybrid","client-device"]"# 299 /// ); 300 /// assert_eq!( 301 /// serde_json::to_string(&Hint::SecurityKeyClientDeviceHybrid)?, 302 /// r#"["security-key","client-device","hybrid"]"# 303 /// ); 304 /// assert_eq!( 305 /// serde_json::to_string(&Hint::SecurityKeyHybridClientDevice)?, 306 /// r#"["security-key","hybrid","client-device"]"# 307 /// ); 308 /// assert_eq!( 309 /// serde_json::to_string(&Hint::ClientDeviceSecurityKeyHybrid)?, 310 /// r#"["client-device","security-key","hybrid"]"# 311 /// ); 312 /// assert_eq!( 313 /// serde_json::to_string(&Hint::ClientDeviceHybridSecurityKey)?, 314 /// r#"["client-device","hybrid","security-key"]"# 315 /// ); 316 /// assert_eq!( 317 /// serde_json::to_string(&Hint::HybridSecurityKeyClientDevice)?, 318 /// r#"["hybrid","security-key","client-device"]"# 319 /// ); 320 /// assert_eq!( 321 /// serde_json::to_string(&Hint::HybridClientDeviceSecurityKey)?, 322 /// r#"["hybrid","client-device","security-key"]"# 323 /// ); 324 /// # Ok::<_, serde_json::Error>(()) 325 /// ``` 326 #[inline] 327 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 328 where 329 S: Serializer, 330 { 331 let count = match *self { 332 Self::None => 0, 333 Self::SecurityKey | Self::ClientDevice | Self::Hybrid => 1, 334 Self::SecurityKeyClientDevice 335 | Self::ClientDeviceSecurityKey 336 | Self::SecurityKeyHybrid 337 | Self::HybridSecurityKey 338 | Self::ClientDeviceHybrid 339 | Self::HybridClientDevice => 2, 340 Self::SecurityKeyClientDeviceHybrid 341 | Self::SecurityKeyHybridClientDevice 342 | Self::ClientDeviceSecurityKeyHybrid 343 | Self::ClientDeviceHybridSecurityKey 344 | Self::HybridSecurityKeyClientDevice 345 | Self::HybridClientDeviceSecurityKey => 3, 346 }; 347 serializer.serialize_seq(Some(count)).and_then(|mut ser| { 348 match *self { 349 Self::None => Ok(()), 350 Self::SecurityKey => ser.serialize_element(SECURITY_KEY), 351 Self::ClientDevice => ser.serialize_element(CLIENT_DEVICE), 352 Self::Hybrid => ser.serialize_element(HYBRID), 353 Self::SecurityKeyClientDevice => ser 354 .serialize_element(SECURITY_KEY) 355 .and_then(|()| ser.serialize_element(CLIENT_DEVICE)), 356 Self::ClientDeviceSecurityKey => ser 357 .serialize_element(CLIENT_DEVICE) 358 .and_then(|()| ser.serialize_element(SECURITY_KEY)), 359 Self::SecurityKeyHybrid => ser 360 .serialize_element(SECURITY_KEY) 361 .and_then(|()| ser.serialize_element(HYBRID)), 362 Self::HybridSecurityKey => ser 363 .serialize_element(HYBRID) 364 .and_then(|()| ser.serialize_element(SECURITY_KEY)), 365 Self::ClientDeviceHybrid => ser 366 .serialize_element(CLIENT_DEVICE) 367 .and_then(|()| ser.serialize_element(HYBRID)), 368 Self::HybridClientDevice => ser 369 .serialize_element(HYBRID) 370 .and_then(|()| ser.serialize_element(CLIENT_DEVICE)), 371 Self::SecurityKeyClientDeviceHybrid => { 372 ser.serialize_element(SECURITY_KEY).and_then(|()| { 373 ser.serialize_element(CLIENT_DEVICE) 374 .and_then(|()| ser.serialize_element(HYBRID)) 375 }) 376 } 377 Self::SecurityKeyHybridClientDevice => { 378 ser.serialize_element(SECURITY_KEY).and_then(|()| { 379 ser.serialize_element(HYBRID) 380 .and_then(|()| ser.serialize_element(CLIENT_DEVICE)) 381 }) 382 } 383 Self::ClientDeviceSecurityKeyHybrid => { 384 ser.serialize_element(CLIENT_DEVICE).and_then(|()| { 385 ser.serialize_element(SECURITY_KEY) 386 .and_then(|()| ser.serialize_element(HYBRID)) 387 }) 388 } 389 Self::ClientDeviceHybridSecurityKey => { 390 ser.serialize_element(CLIENT_DEVICE).and_then(|()| { 391 ser.serialize_element(HYBRID) 392 .and_then(|()| ser.serialize_element(SECURITY_KEY)) 393 }) 394 } 395 Self::HybridSecurityKeyClientDevice => { 396 ser.serialize_element(HYBRID).and_then(|()| { 397 ser.serialize_element(SECURITY_KEY) 398 .and_then(|()| ser.serialize_element(CLIENT_DEVICE)) 399 }) 400 } 401 Self::HybridClientDeviceSecurityKey => { 402 ser.serialize_element(HYBRID).and_then(|()| { 403 ser.serialize_element(CLIENT_DEVICE) 404 .and_then(|()| ser.serialize_element(SECURITY_KEY)) 405 }) 406 } 407 } 408 .and_then(|()| ser.end()) 409 }) 410 } 411 } 412 /// `"first"`. 413 const FIRST: &str = "first"; 414 /// `"second"`. 415 const SECOND: &str = "second"; 416 impl Serialize for PrfInput<'_, '_> { 417 /// Serializes `self` to conform with 418 /// [`AuthenticationExtensionsPRFValues`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfvalues). 419 /// 420 /// # Examples 421 /// 422 /// ``` 423 /// # use webauthn_rp::request::{PrfInput, ExtensionReq}; 424 /// assert_eq!( 425 /// serde_json::to_string(&PrfInput { 426 /// first: [0; 4].as_slice(), 427 /// second: Some([2; 1].as_slice()), 428 /// })?, 429 /// r#"{"first":"AAAAAA","second":"Ag"}"# 430 /// ); 431 /// # Ok::<_, serde_json::Error>(()) 432 /// ``` 433 #[expect( 434 clippy::arithmetic_side_effects, 435 reason = "comment justifies how overflow is not possible" 436 )] 437 #[inline] 438 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 439 where 440 S: Serializer, 441 { 442 serializer 443 // The max value is 1 + 1 = 2, so overflow is not an issue. 444 .serialize_struct("PrfInput", 1 + usize::from(self.second.is_some())) 445 .and_then(|mut ser| { 446 ser.serialize_field(FIRST, base64url_nopad::encode(self.first).as_str()) 447 .and_then(|()| { 448 self.second 449 .as_ref() 450 .map_or(Ok(()), |second| { 451 ser.serialize_field( 452 SECOND, 453 base64url_nopad::encode(second).as_str(), 454 ) 455 }) 456 .and_then(|()| ser.end()) 457 }) 458 }) 459 } 460 } 461 impl<'de> Deserialize<'de> for PrfInputOwned { 462 /// Deserializes a `struct` based on 463 /// [`AuthenticationExtensionsPRFValues`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfvalues). 464 /// 465 /// [`first`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-first) is required and 466 /// must not be `null`. 467 /// [`second`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-second) is not required 468 /// and can be `null`. 469 /// 470 /// Note [`PrfInputOwned::ext_req`] is set to [`ExtensionReq::Allow`]. 471 #[inline] 472 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 473 where 474 D: Deserializer<'de>, 475 { 476 /// `Visitor` for `PrfInputOwned`. 477 struct PrfInputOwnedVisitor; 478 impl<'d> Visitor<'d> for PrfInputOwnedVisitor { 479 type Value = PrfInputOwned; 480 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 481 formatter.write_str("PrfInputOwned") 482 } 483 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 484 where 485 A: MapAccess<'d>, 486 { 487 /// Field for `PrfInputOwned`. 488 enum Field { 489 /// `first`. 490 First, 491 /// `second`. 492 Second, 493 } 494 impl<'e> Deserialize<'e> for Field { 495 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 496 where 497 D: Deserializer<'e>, 498 { 499 /// `Visitor` for `Field`. 500 struct FieldVisitor; 501 impl Visitor<'_> for FieldVisitor { 502 type Value = Field; 503 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 504 write!(formatter, "'{FIRST}' or '{SECOND}'") 505 } 506 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 507 where 508 E: Error, 509 { 510 match v { 511 FIRST => Ok(Field::First), 512 SECOND => Ok(Field::Second), 513 _ => Err(E::unknown_field(v, FIELDS)), 514 } 515 } 516 } 517 deserializer.deserialize_identifier(FieldVisitor) 518 } 519 } 520 let mut fst = None; 521 let mut snd = None; 522 while let Some(key) = map.next_key()? { 523 match key { 524 Field::First => { 525 if fst.is_some() { 526 return Err(Error::duplicate_field(FIRST)); 527 } 528 fst = map 529 .next_value::<Base64DecodedVal>() 530 .map(|val| Some(val.0))?; 531 } 532 Field::Second => { 533 if snd.is_some() { 534 return Err(Error::duplicate_field(SECOND)); 535 } 536 snd = map 537 .next_value::<Option<Base64DecodedVal>>() 538 .map(|opt| Some(opt.map(|val| val.0)))?; 539 } 540 } 541 } 542 fst.ok_or_else(|| Error::missing_field(FIRST)) 543 .map(|first| PrfInputOwned { 544 first, 545 second: snd.flatten(), 546 ext_req: ExtensionReq::Allow, 547 }) 548 } 549 } 550 const FIELDS: &[&str; 2] = &[FIRST, SECOND]; 551 deserializer.deserialize_struct("PrfInputOwned", FIELDS, PrfInputOwnedVisitor) 552 } 553 } 554 impl<'de> Deserialize<'de> for AsciiDomain { 555 /// Deserializes [`String`] based on [`Self::try_from`]. 556 /// 557 /// # Examples 558 /// 559 /// ``` 560 /// # use webauthn_rp::request::AsciiDomain; 561 /// assert!(matches!( 562 /// serde_json::from_str::<AsciiDomain>(r#""example.com""#)?.as_ref(), 563 /// "example.com" 564 /// )); 565 /// # Ok::<_, serde_json::Error>(()) 566 /// ``` 567 #[inline] 568 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 569 where 570 D: Deserializer<'de>, 571 { 572 String::deserialize(deserializer).and_then(|dom| Self::try_from(dom).map_err(Error::custom)) 573 } 574 } 575 impl Deserialize<'static> for AsciiDomainStatic { 576 /// Deserializes [`prim@str`] based on [`Self::new`]. 577 /// 578 /// # Examples 579 /// 580 /// ``` 581 /// # use webauthn_rp::request::AsciiDomainStatic; 582 /// assert!(matches!( 583 /// serde_json::from_str::<AsciiDomainStatic>(r#""example.com""#)?.as_str(), 584 /// "example.com" 585 /// )); 586 /// # Ok::<_, serde_json::Error>(()) 587 /// ``` 588 #[inline] 589 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 590 where 591 D: Deserializer<'static>, 592 { 593 <&'static str>::deserialize(deserializer).and_then(|dom| { 594 Self::new(dom) 595 .ok_or_else(|| Error::custom("AsciiDomainStatic requires a valid ASCII domain")) 596 }) 597 } 598 } 599 impl<'de> Deserialize<'de> for Url { 600 /// Deserializes [`prim@str`] based on [`Self::from_str`]. 601 /// 602 /// # Examples 603 /// 604 /// ``` 605 /// # use webauthn_rp::request::Url; 606 /// assert!(matches!( 607 /// serde_json::from_str::<Url>(r#""ssh:foo""#)?.as_ref(), 608 /// "ssh:foo" 609 /// )); 610 /// # Ok::<_, serde_json::Error>(()) 611 /// ``` 612 #[inline] 613 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 614 where 615 D: Deserializer<'de>, 616 { 617 /// `Visitor` for `Url` 618 struct UrlVisitor; 619 impl Visitor<'_> for UrlVisitor { 620 type Value = Url; 621 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 622 formatter.write_str("Url") 623 } 624 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 625 where 626 E: Error, 627 { 628 Url::from_str(v).map_err(E::custom) 629 } 630 } 631 deserializer.deserialize_str(UrlVisitor) 632 } 633 } 634 impl<'de> Deserialize<'de> for RpId { 635 /// Deserializes a [`String`] based on [`Self::try_from`]. 636 /// 637 /// # Examples 638 /// 639 /// ``` 640 /// # use webauthn_rp::request::RpId; 641 /// assert!(matches!( 642 /// serde_json::from_str::<RpId>(r#""example.com""#)?.as_ref(), 643 /// "example.com" 644 /// )); 645 /// assert!(matches!( 646 /// serde_json::from_str::<RpId>(r#""ssh:foo""#)?.as_ref(), 647 /// "ssh:foo" 648 /// )); 649 /// # Ok::<_, serde_json::Error>(()) 650 /// ``` 651 #[inline] 652 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 653 where 654 D: Deserializer<'de>, 655 { 656 String::deserialize(deserializer).and_then(|dom| Self::try_from(dom).map_err(Error::custom)) 657 } 658 } 659 /// Helper for `Hint::deserialize`. 660 enum HintHelper { 661 /// `"security-key"` 662 SecurityKey, 663 /// `"client-device"` 664 ClientDevice, 665 /// `"hybrid"` 666 Hybrid, 667 } 668 impl<'de> Deserialize<'de> for HintHelper { 669 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 670 where 671 D: Deserializer<'de>, 672 { 673 /// `Visitor` for `HintHelper`. 674 struct HintHelperVisitor; 675 impl Visitor<'_> for HintHelperVisitor { 676 type Value = HintHelper; 677 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 678 write!( 679 formatter, 680 "'{SECURITY_KEY}', '{CLIENT_DEVICE}', or '{HYBRID}'" 681 ) 682 } 683 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 684 where 685 E: Error, 686 { 687 match v { 688 SECURITY_KEY => Ok(HintHelper::SecurityKey), 689 CLIENT_DEVICE => Ok(HintHelper::ClientDevice), 690 HYBRID => Ok(HintHelper::Hybrid), 691 _ => Err(E::invalid_value( 692 Unexpected::Str(v), 693 &format!("'{SECURITY_KEY}', '{CLIENT_DEVICE}', or '{HYBRID}'").as_str(), 694 )), 695 } 696 } 697 } 698 deserializer.deserialize_str(HintHelperVisitor) 699 } 700 } 701 impl<'de> Deserialize<'de> for Hint { 702 /// Deserializes a sequence based on 703 /// [`hints`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptionsjson-hints). 704 /// 705 /// Note duplicates and unknown values will lead to an error. 706 /// 707 /// # Examples 708 /// 709 /// ``` 710 /// # use webauthn_rp::request::Hint; 711 /// assert!( 712 /// matches!( 713 /// serde_json::from_str(r#"["security-key", "hybrid", "client-device"]"#)?, 714 /// Hint::SecurityKeyHybridClientDevice 715 /// ) 716 /// ); 717 /// assert!( 718 /// matches!( 719 /// serde_json::from_str(r#"["hybrid", "security-key", "client-device"]"#)?, 720 /// Hint::HybridSecurityKeyClientDevice 721 /// ) 722 /// ); 723 /// assert!( 724 /// matches!( 725 /// serde_json::from_str(r#"[]"#)?, 726 /// Hint::None 727 /// ) 728 /// ); 729 /// assert!( 730 /// serde_json::from_str::<Hint>(r#"["security-key", "hybrid", "hybrid"]"#).is_err() 731 /// ); 732 /// # Ok::<_, serde_json::Error>(()) 733 /// ``` 734 #[inline] 735 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 736 where 737 D: Deserializer<'de>, 738 { 739 /// `Visitor` for `Hint`. 740 struct HintVisitor; 741 impl<'d> Visitor<'d> for HintVisitor { 742 type Value = Hint; 743 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 744 formatter.write_str("unique sequence of hints") 745 } 746 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> 747 where 748 A: SeqAccess<'d>, 749 { 750 let mut hint = Hint::None; 751 while let Some(elem) = seq.next_element::<HintHelper>()? { 752 hint = match elem { 753 HintHelper::SecurityKey => match hint { 754 Hint::None => Hint::SecurityKey, 755 Hint::SecurityKey 756 | Hint::SecurityKeyClientDevice 757 | Hint::ClientDeviceSecurityKey 758 | Hint::SecurityKeyHybrid 759 | Hint::HybridSecurityKey 760 | Hint::SecurityKeyClientDeviceHybrid 761 | Hint::SecurityKeyHybridClientDevice 762 | Hint::ClientDeviceSecurityKeyHybrid 763 | Hint::ClientDeviceHybridSecurityKey 764 | Hint::HybridSecurityKeyClientDevice 765 | Hint::HybridClientDeviceSecurityKey => { 766 return Err(Error::custom(format!( 767 "'{SECURITY_KEY}' hint appeared more than once" 768 ))); 769 } 770 Hint::ClientDevice => Hint::ClientDeviceSecurityKey, 771 Hint::Hybrid => Hint::HybridSecurityKey, 772 Hint::ClientDeviceHybrid => Hint::ClientDeviceHybridSecurityKey, 773 Hint::HybridClientDevice => Hint::HybridClientDeviceSecurityKey, 774 }, 775 HintHelper::ClientDevice => match hint { 776 Hint::None => Hint::ClientDevice, 777 Hint::SecurityKey => Hint::SecurityKeyClientDevice, 778 Hint::ClientDevice 779 | Hint::ClientDeviceSecurityKey 780 | Hint::SecurityKeyClientDevice 781 | Hint::ClientDeviceHybrid 782 | Hint::HybridClientDevice 783 | Hint::ClientDeviceSecurityKeyHybrid 784 | Hint::ClientDeviceHybridSecurityKey 785 | Hint::SecurityKeyClientDeviceHybrid 786 | Hint::SecurityKeyHybridClientDevice 787 | Hint::HybridClientDeviceSecurityKey 788 | Hint::HybridSecurityKeyClientDevice => { 789 return Err(Error::custom(format!( 790 "'{CLIENT_DEVICE}' hint appeared more than once" 791 ))); 792 } 793 Hint::Hybrid => Hint::HybridClientDevice, 794 Hint::SecurityKeyHybrid => Hint::SecurityKeyHybridClientDevice, 795 Hint::HybridSecurityKey => Hint::HybridSecurityKeyClientDevice, 796 }, 797 HintHelper::Hybrid => match hint { 798 Hint::None => Hint::Hybrid, 799 Hint::Hybrid 800 | Hint::HybridClientDevice 801 | Hint::ClientDeviceHybrid 802 | Hint::HybridSecurityKey 803 | Hint::SecurityKeyHybrid 804 | Hint::HybridClientDeviceSecurityKey 805 | Hint::HybridSecurityKeyClientDevice 806 | Hint::ClientDeviceHybridSecurityKey 807 | Hint::ClientDeviceSecurityKeyHybrid 808 | Hint::SecurityKeyHybridClientDevice 809 | Hint::SecurityKeyClientDeviceHybrid => { 810 return Err(Error::custom(format!( 811 "'{HYBRID}' hint appeared more than once" 812 ))); 813 } 814 Hint::ClientDevice => Hint::ClientDeviceHybrid, 815 Hint::SecurityKey => Hint::SecurityKeyHybrid, 816 Hint::ClientDeviceSecurityKey => Hint::ClientDeviceSecurityKeyHybrid, 817 Hint::SecurityKeyClientDevice => Hint::SecurityKeyClientDeviceHybrid, 818 }, 819 }; 820 } 821 Ok(hint) 822 } 823 } 824 deserializer.deserialize_seq(HintVisitor) 825 } 826 } 827 impl<'de> Deserialize<'de> for CredentialMediationRequirement { 828 /// Deserializes a [`prim@str`] based on 829 /// [`CredentialMediationRequirement`](https://www.w3.org/TR/credential-management-1/#enumdef-credentialmediationrequirement). 830 /// 831 /// # Examples 832 /// 833 /// ``` 834 /// # use webauthn_rp::request::CredentialMediationRequirement; 835 /// assert!( 836 /// matches!( 837 /// serde_json::from_str(r#""required""#)?, 838 /// CredentialMediationRequirement::Required, 839 /// ) 840 /// ); 841 /// assert!( 842 /// matches!( 843 /// serde_json::from_str(r#""conditional""#)?, 844 /// CredentialMediationRequirement::Conditional, 845 /// ) 846 /// ); 847 /// # Ok::<_, serde_json::Error>(()) 848 /// ``` 849 #[inline] 850 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 851 where 852 D: Deserializer<'de>, 853 { 854 /// `Visitor` for `CredentialMediationRequirement`. 855 struct CredentialMediationRequirementVisitor; 856 impl Visitor<'_> for CredentialMediationRequirementVisitor { 857 type Value = CredentialMediationRequirement; 858 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 859 write!(formatter, "'{REQUIRED}' or '{CONDITIONAL}'") 860 } 861 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 862 where 863 E: Error, 864 { 865 match v { 866 REQUIRED => Ok(CredentialMediationRequirement::Required), 867 CONDITIONAL => Ok(CredentialMediationRequirement::Conditional), 868 _ => Err(E::invalid_value( 869 Unexpected::Str(v), 870 &format!("'{REQUIRED}' or '{CONDITIONAL}'").as_str(), 871 )), 872 } 873 } 874 } 875 deserializer.deserialize_str(CredentialMediationRequirementVisitor) 876 } 877 } 878 /// Helper to deserialize the prf extension. 879 pub(super) struct PrfHelper(pub PrfInputOwned); 880 impl<'e> Deserialize<'e> for PrfHelper { 881 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 882 where 883 D: Deserializer<'e>, 884 { 885 /// `Visitor` for `PrfHelper`. 886 struct PrfHelperVisitor; 887 impl<'f> Visitor<'f> for PrfHelperVisitor { 888 type Value = PrfHelper; 889 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 890 formatter.write_str("Prf") 891 } 892 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 893 where 894 A: MapAccess<'f>, 895 { 896 /// Field for `PrfHelper`. 897 struct Field; 898 impl<'g> Deserialize<'g> for Field { 899 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 900 where 901 D: Deserializer<'g>, 902 { 903 /// `Visitor` for `Field`. 904 struct FieldVisitor; 905 impl Visitor<'_> for FieldVisitor { 906 type Value = Field; 907 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 908 write!(formatter, "'{EVAL}'") 909 } 910 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 911 where 912 E: Error, 913 { 914 if v == EVAL { 915 Ok(Field) 916 } else { 917 Err(E::unknown_field(v, FIELDS)) 918 } 919 } 920 } 921 deserializer.deserialize_identifier(FieldVisitor) 922 } 923 } 924 map.next_key::<Field>().and_then(|opt_key| { 925 opt_key 926 .ok_or_else(|| Error::missing_field(EVAL)) 927 .and_then(|_k| { 928 map.next_value().and_then(|prf_input| { 929 map.next_key::<Field>().and_then(|opt_key2| { 930 opt_key2.map_or_else( 931 || Ok(PrfHelper(prf_input)), 932 |_k2| Err(Error::duplicate_field(EVAL)), 933 ) 934 }) 935 }) 936 }) 937 }) 938 } 939 } 940 /// `"eval"`. 941 const EVAL: &str = "eval"; 942 /// Fields for `PrfHelper` 943 const FIELDS: &[&str; 1] = &[EVAL]; 944 deserializer.deserialize_struct("Prf", FIELDS, PrfHelperVisitor) 945 } 946 } 947 /// Default RP ID to use containing the value `"example.invalid"` when an RP ID is not sent. 948 pub(super) const DEFAULT_RP_ID: RpId = 949 RpId::StaticDomain(AsciiDomainStatic::new("example.invalid").unwrap());