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