ser.rs (48010B)
1 #![expect( 2 clippy::question_mark_used, 3 reason = "noisy, opinionated, and likely doesn't prevent bugs or improve APIs" 4 )] 5 extern crate alloc; 6 use super::{ 7 AllAcceptedCredentialsOptions, AuthTransports, AuthenticatorAttachment, AuthenticatorTransport, 8 Challenge, CredentialId, CurrentUserDetailsOptions, Origin, SentChallenge, 9 }; 10 use alloc::borrow::Cow; 11 use core::{ 12 fmt::{self, Formatter}, 13 marker::PhantomData, 14 }; 15 use data_encoding::BASE64URL_NOPAD; 16 use serde::{ 17 de::{Deserialize, Deserializer, Error, IgnoredAny, MapAccess, SeqAccess, Unexpected, Visitor}, 18 ser::{Serialize, SerializeSeq as _, SerializeStruct as _, Serializer}, 19 }; 20 /// [`"ble"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-ble). 21 const BLE: &str = "ble"; 22 /// [`"hybrid"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-hybrid). 23 const HYBRID: &str = "hybrid"; 24 /// [`"internal"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-internal). 25 const INTERNAL: &str = "internal"; 26 /// [`"nfc"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-nfc). 27 const NFC: &str = "nfc"; 28 /// [`"smart-card"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-smart-card). 29 const SMART_CARD: &str = "smart-card"; 30 /// [`"usb"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-usb). 31 const USB: &str = "usb"; 32 impl Serialize for AuthenticatorTransport { 33 /// Serializes `self` as 34 /// [`AuthenticatorTransport`](https://www.w3.org/TR/webauthn-3/#enumdef-authenticatortransport). 35 /// 36 /// # Examples 37 /// 38 /// ``` 39 /// # use webauthn_rp::response::AuthenticatorTransport; 40 /// assert_eq!( 41 /// serde_json::to_string(&AuthenticatorTransport::Usb)?, 42 /// r#""usb""# 43 /// ); 44 /// # Ok::<_, serde_json::Error>(()) 45 /// ``` 46 #[inline] 47 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 48 where 49 S: Serializer, 50 { 51 serializer.serialize_str(match *self { 52 Self::Ble => BLE, 53 Self::Hybrid => HYBRID, 54 Self::Internal => INTERNAL, 55 Self::Nfc => NFC, 56 Self::SmartCard => SMART_CARD, 57 Self::Usb => USB, 58 }) 59 } 60 } 61 impl<'de> Deserialize<'de> for AuthenticatorTransport { 62 /// Deserializes [`prim@str`] based on 63 /// [`AuthenticatorTransport`](https://www.w3.org/TR/webauthn-3/#enumdef-authenticatortransport). 64 /// 65 /// # Examples 66 /// 67 /// ``` 68 /// # use webauthn_rp::response::AuthenticatorTransport; 69 /// assert!(matches!( 70 /// serde_json::from_str::<AuthenticatorTransport>(r#""usb""#)?, 71 /// AuthenticatorTransport::Usb 72 /// )); 73 /// // Case matters. 74 /// assert!(serde_json::from_str::<AuthenticatorTransport>(r#""Usb""#).is_err()); 75 /// # Ok::<_, serde_json::Error>(()) 76 /// ``` 77 #[inline] 78 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 79 where 80 D: Deserializer<'de>, 81 { 82 /// `Visitor` for `AuthenticatorTransport`. 83 struct AuthenticatorTransportVisitor; 84 impl Visitor<'_> for AuthenticatorTransportVisitor { 85 type Value = AuthenticatorTransport; 86 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 87 formatter.write_str("AuthenticatorTransport") 88 } 89 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 90 where 91 E: Error, 92 { 93 match v { 94 BLE => Ok(AuthenticatorTransport::Ble), 95 HYBRID => Ok(AuthenticatorTransport::Hybrid), 96 INTERNAL => Ok(AuthenticatorTransport::Internal), 97 NFC => Ok(AuthenticatorTransport::Nfc), 98 SMART_CARD => Ok(AuthenticatorTransport::SmartCard), 99 USB => Ok(AuthenticatorTransport::Usb), 100 _ => Err(E::invalid_value( 101 Unexpected::Str(v), 102 &format!( 103 "'{BLE}', '{HYBRID}', '{INTERNAL}', '{NFC}', '{SMART_CARD}', or '{USB}'" 104 ) 105 .as_str(), 106 )), 107 } 108 } 109 } 110 deserializer.deserialize_str(AuthenticatorTransportVisitor) 111 } 112 } 113 impl Serialize for AuthTransports { 114 /// Serializes `self` based on 115 /// [`transports`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptor-transports). 116 /// 117 /// # Examples 118 /// 119 /// ``` 120 /// # use webauthn_rp::response::{AuthTransports, AuthenticatorTransport}; 121 /// # #[cfg(feature = "custom")] 122 /// assert_eq!( 123 /// serde_json::to_string(&AuthTransports::ALL)?, 124 /// r#"["ble","hybrid","internal","nfc","smart-card","usb"]"# 125 /// ); 126 /// # Ok::<_, serde_json::Error>(()) 127 /// ``` 128 #[expect(clippy::unreachable, reason = "there is a bug, so we want to crash")] 129 #[inline] 130 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 131 where 132 S: Serializer, 133 { 134 let count = usize::try_from(self.count()) 135 .unwrap_or_else(|_e| unreachable!("there is a bug in AuthenticatorTransports::count")); 136 serializer.serialize_seq(Some(count)).and_then(|mut ser| { 137 if self.contains(AuthenticatorTransport::Ble) { 138 ser.serialize_element(&AuthenticatorTransport::Ble) 139 } else { 140 Ok(()) 141 } 142 .and_then(|()| { 143 if self.contains(AuthenticatorTransport::Hybrid) { 144 ser.serialize_element(&AuthenticatorTransport::Hybrid) 145 } else { 146 Ok(()) 147 } 148 .and_then(|()| { 149 if self.contains(AuthenticatorTransport::Internal) { 150 ser.serialize_element(&AuthenticatorTransport::Internal) 151 } else { 152 Ok(()) 153 } 154 .and_then(|()| { 155 if self.contains(AuthenticatorTransport::Nfc) { 156 ser.serialize_element(&AuthenticatorTransport::Nfc) 157 } else { 158 Ok(()) 159 } 160 .and_then(|()| { 161 if self.contains(AuthenticatorTransport::SmartCard) { 162 ser.serialize_element(&AuthenticatorTransport::SmartCard) 163 } else { 164 Ok(()) 165 } 166 .and_then(|()| { 167 if self.contains(AuthenticatorTransport::Usb) { 168 ser.serialize_element(&AuthenticatorTransport::Usb) 169 } else { 170 Ok(()) 171 } 172 .and_then(|()| ser.end()) 173 }) 174 }) 175 }) 176 }) 177 }) 178 }) 179 } 180 } 181 impl<'de> Deserialize<'de> for AuthTransports { 182 /// Deserializes a sequence based on 183 /// [`transports`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptor-transports). 184 /// 185 /// # Examples 186 /// 187 /// ``` 188 /// # use webauthn_rp::response::{AuthTransports, AuthenticatorTransport}; 189 /// # #[cfg(feature = "custom")] 190 /// assert_eq!( 191 /// serde_json::from_str::<AuthTransports>( 192 /// r#"["ble","hybrid","internal","nfc","smart-card","usb"]"# 193 /// ) 194 /// ?.count(), 195 /// 6 196 /// ); 197 /// // Errors since `"foo"` is not valid. 198 /// assert!(serde_json::from_str::<AuthTransports>(r#"["foo"]"#).is_err()); 199 /// # Ok::<_, serde_json::Error>(()) 200 /// ``` 201 #[inline] 202 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 203 where 204 D: Deserializer<'de>, 205 { 206 /// `Visitor` for `AuthTransports`. 207 struct AuthTransportsVisitor; 208 impl<'d> Visitor<'d> for AuthTransportsVisitor { 209 type Value = AuthTransports; 210 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 211 formatter.write_str("AuthTransports") 212 } 213 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> 214 where 215 A: SeqAccess<'d>, 216 { 217 let mut transports = AuthTransports::new(); 218 while let Some(val) = seq.next_element::<AuthenticatorTransport>()? { 219 transports = transports.add_transport(val); 220 } 221 Ok(transports) 222 } 223 } 224 deserializer.deserialize_seq(AuthTransportsVisitor) 225 } 226 } 227 impl<T: AsRef<[u8]>> Serialize for CredentialId<T> { 228 /// Serializes `self` into a [`prim@str`] based on 229 /// [`id`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptorjson-id). 230 /// 231 /// # Examples 232 /// 233 /// ``` 234 /// # use webauthn_rp::response::CredentialId; 235 /// // `CredentialId::try_from` only exists when `custom` is enabled; and even then, it is 236 /// // likely never needed since the `CredentialId` was originally sent from the client and is likely 237 /// // stored in a database which would be fetched by `UserHandle` or `Authentication::raw_id`. 238 /// # #[cfg(feature = "custom")] 239 /// assert_eq!( 240 /// serde_json::to_string(&CredentialId::try_from(vec![0; 16])?).unwrap(), 241 /// r#""AAAAAAAAAAAAAAAAAAAAAA""# 242 /// ); 243 /// # Ok::<_, webauthn_rp::AggErr>(()) 244 ///``` 245 #[inline] 246 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 247 where 248 S: Serializer, 249 { 250 serializer.serialize_str(BASE64URL_NOPAD.encode(self.0.as_ref()).as_str()) 251 } 252 } 253 impl<'de> Deserialize<'de> for CredentialId<Vec<u8>> { 254 /// Deserializes [`prim@str`] based on 255 /// [`id`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptorjson-id). 256 /// 257 /// # Examples 258 /// 259 /// ``` 260 /// # use webauthn_rp::response::CredentialId; 261 /// # #[cfg(feature = "custom")] 262 /// assert_eq!( 263 /// serde_json::from_str::<CredentialId<_>>(r#""AAAAAAAAAAAAAAAAAAAAAA""#).unwrap(), 264 /// CredentialId::try_from(vec![0; 16])? 265 /// ); 266 /// # Ok::<_, webauthn_rp::AggErr>(()) 267 ///``` 268 #[inline] 269 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 270 where 271 D: Deserializer<'de>, 272 { 273 /// `Visitor` for `CredentialId`. 274 struct CredentialIdVisitor; 275 impl Visitor<'_> for CredentialIdVisitor { 276 type Value = CredentialId<Vec<u8>>; 277 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 278 formatter.write_str("CredentialId") 279 } 280 #[expect(clippy::unreachable, reason = "we want to crash when there is a bug")] 281 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 282 where 283 E: Error, 284 { 285 // Any value between `super::CRED_ID_MIN_LEN` and `super::CRED_ID_MIN_LEN` can be base64url encoded 286 // without fear since that range is just 16 to 1023, and 287 // 4/3 of 1023 is less than `usize::MAX`. 288 if (crate::base64url_nopad_len(super::CRED_ID_MIN_LEN).unwrap_or_else(|| { 289 unreachable!("there is a bug in webauthn_rp::base64url_nopad_len") 290 }) 291 ..=crate::base64url_nopad_len(super::CRED_ID_MAX_LEN).unwrap_or_else(|| { 292 unreachable!("there is a bug in webauthn_rp::base64url_nopad_len") 293 })) 294 .contains(&v.len()) 295 { 296 BASE64URL_NOPAD 297 .decode(v.as_bytes()) 298 .map_err(E::custom) 299 .map(CredentialId) 300 } else { 301 Err(E::invalid_value( 302 Unexpected::Str(v), 303 &"16 to 1023 bytes encoded in base64url without padding", 304 )) 305 } 306 } 307 } 308 deserializer.deserialize_str(CredentialIdVisitor) 309 } 310 } 311 impl<'de> Deserialize<'de> for AuthenticatorAttachment { 312 /// Deserializes [`prim@str`] based on 313 /// [`AuthenticatorAttachment`](https://www.w3.org/TR/webauthn-3/#enumdef-authenticatorattachment). 314 /// 315 /// # Examples 316 /// 317 /// ``` 318 /// # use webauthn_rp::response::AuthenticatorAttachment; 319 /// assert!(matches!( 320 /// serde_json::from_str::<AuthenticatorAttachment>(r#""cross-platform""#)?, 321 /// AuthenticatorAttachment::CrossPlatform) 322 /// ); 323 /// assert!(matches!( 324 /// serde_json::from_str::<AuthenticatorAttachment>(r#""platform""#)?, 325 /// AuthenticatorAttachment::Platform) 326 /// ); 327 /// // Case matters. 328 /// assert!(serde_json::from_str::<AuthenticatorAttachment>(r#""Platform""#).is_err()); 329 /// // `AuthenticatorAttachment::None` is not deserializable. 330 /// assert!(serde_json::from_str::<AuthenticatorAttachment>(r#""""#).is_err()); 331 /// assert!(serde_json::from_str::<AuthenticatorAttachment>("null").is_err()); 332 /// assert!(serde_json::from_str::<AuthenticatorAttachment>(r#""none""#).is_err()); 333 /// assert!(serde_json::from_str::<AuthenticatorAttachment>(r#""None""#).is_err()); 334 /// # Ok::<_, serde_json::Error>(()) 335 ///``` 336 #[inline] 337 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 338 where 339 D: Deserializer<'de>, 340 { 341 /// `Visitor` for `AuthenticatorAttachment`. 342 struct AuthenticatorAttachmentVisitor; 343 impl Visitor<'_> for AuthenticatorAttachmentVisitor { 344 type Value = AuthenticatorAttachment; 345 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 346 formatter.write_str("AuthenticatorAttachment") 347 } 348 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 349 where 350 E: Error, 351 { 352 /// `"platform"` 353 const PLATFORM: &str = "platform"; 354 /// `"cross-platform"` 355 const CROSS_PLATFORM: &str = "cross-platform"; 356 match v { 357 PLATFORM => Ok(AuthenticatorAttachment::Platform), 358 CROSS_PLATFORM => Ok(AuthenticatorAttachment::CrossPlatform), 359 _ => Err(E::invalid_value( 360 Unexpected::Str(v), 361 &format!("'{PLATFORM}' or '{CROSS_PLATFORM}'").as_str(), 362 )), 363 } 364 } 365 } 366 deserializer.deserialize_str(AuthenticatorAttachmentVisitor) 367 } 368 } 369 /// Container of data that was encoded in base64url. 370 pub(super) struct Base64DecodedVal(pub Vec<u8>); 371 impl<'de> Deserialize<'de> for Base64DecodedVal { 372 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 373 where 374 D: Deserializer<'de>, 375 { 376 /// `Visitor` for `Base64DecodedVal`. 377 struct Base64DecodedValVisitor; 378 impl Visitor<'_> for Base64DecodedValVisitor { 379 type Value = Base64DecodedVal; 380 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 381 formatter.write_str("base64url-encoded data") 382 } 383 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 384 where 385 E: Error, 386 { 387 BASE64URL_NOPAD 388 .decode(v.as_bytes()) 389 .map_err(E::custom) 390 .map(Base64DecodedVal) 391 } 392 } 393 deserializer.deserialize_str(Base64DecodedValVisitor) 394 } 395 } 396 impl<'de> Deserialize<'de> for SentChallenge { 397 /// Deserializes `[u8]` or [`prim@str`] based on 398 /// [`challenge`](https://www.w3.org/TR/webauthn-3/#dom-collectedclientdata-challenge). 399 /// 400 /// Specifically a `[u8]` or `str` is base64url-decoded and interpreted as a little-endian 401 /// `u128`. 402 /// 403 /// # Examples 404 /// 405 /// ``` 406 /// # use webauthn_rp::response::SentChallenge; 407 /// assert_eq!( 408 /// serde_json::from_slice::<SentChallenge>(br#""AAAAAAAAAAAAAAAAAAAAAA""#)?, 409 /// SentChallenge(0) 410 /// ); 411 /// # Ok::<_, serde_json::Error>(()) 412 ///``` 413 #[inline] 414 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 415 where 416 D: Deserializer<'de>, 417 { 418 /// `Visitor` for `SentChallenge`. 419 struct ChallengeVisitor; 420 impl Visitor<'_> for ChallengeVisitor { 421 type Value = SentChallenge; 422 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 423 formatter.write_str( 424 "base64 encoding of the 16-byte challenge in a URL safe way without padding", 425 ) 426 } 427 #[expect( 428 clippy::panic_in_result_fn, 429 reason = "we want to crash when there is a bug" 430 )] 431 #[expect( 432 clippy::little_endian_bytes, 433 reason = "SentChallenge::deserialize and Challenge::serialize need to be consistent across architectures" 434 )] 435 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> 436 where 437 E: Error, 438 { 439 if v.len() == Challenge::BASE64_LEN { 440 let mut data = [0; 16]; 441 BASE64URL_NOPAD 442 .decode_mut(v, data.as_mut_slice()) 443 .map_err(|err| E::custom(err.error)) 444 .map(|len| { 445 assert_eq!(len, 16, "there is a bug in BASE64URL_NOPAD::decode_mut"); 446 SentChallenge(u128::from_le_bytes(data)) 447 }) 448 } else { 449 Err(E::invalid_value( 450 Unexpected::Bytes(v), 451 &"22 bytes encoded in base64url without padding", 452 )) 453 } 454 } 455 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 456 where 457 E: Error, 458 { 459 self.visit_bytes(v.as_bytes()) 460 } 461 } 462 deserializer.deserialize_bytes(ChallengeVisitor) 463 } 464 } 465 impl<'de: 'a, 'a> Deserialize<'de> for Origin<'a> { 466 /// Deserializes [`prim@str`] by borrowing the data when possible. 467 /// 468 /// # Examples 469 /// 470 /// ``` 471 /// # extern crate alloc; 472 /// # use alloc::borrow::Cow; 473 /// # use webauthn_rp::response::Origin; 474 /// let origin_borrowed = "https://example.com"; 475 /// let origin_owned = "\\\\https://example.com"; 476 /// assert!( 477 /// matches!(serde_json::from_str::<Origin<'_>>(format!("\"{origin_borrowed}\"").as_str())?.0, Cow::Borrowed(val) if val == origin_borrowed) 478 /// ); 479 /// assert!( 480 /// matches!(serde_json::from_str::<Origin<'_>>(format!("\"{origin_owned}\"").as_str())?.0, Cow::Owned(val) if *val.as_bytes() == origin_owned.as_bytes()[1..]) 481 /// ); 482 /// # Ok::<_, serde_json::Error>(()) 483 ///``` 484 #[inline] 485 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 486 where 487 D: Deserializer<'de>, 488 { 489 /// `Visitor` for `Origin`. 490 struct OriginVisitor<'b>(PhantomData<fn() -> &'b ()>); 491 impl<'d: 'b, 'b> Visitor<'d> for OriginVisitor<'b> { 492 type Value = Origin<'b>; 493 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 494 formatter.write_str("Origin") 495 } 496 fn visit_borrowed_str<E>(self, v: &'d str) -> Result<Self::Value, E> 497 where 498 E: Error, 499 { 500 Ok(Origin(Cow::Borrowed(v))) 501 } 502 fn visit_string<E>(self, v: String) -> Result<Self::Value, E> 503 where 504 E: Error, 505 { 506 Ok(Origin(Cow::Owned(v))) 507 } 508 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 509 where 510 E: Error, 511 { 512 self.visit_string(v.to_owned()) 513 } 514 } 515 deserializer.deserialize_str(OriginVisitor(PhantomData)) 516 } 517 } 518 /// `trait` that returns an empty instance of `Self`. 519 pub(super) trait ClientExtensions: Sized { 520 /// Returns an empty instance of `Self`. 521 fn empty() -> Self; 522 } 523 /// Response for both registration and authentication ceremonies. 524 /// 525 /// [`Self::raw_id`] is always `Some` when `!RELAXED` or `!REG`. 526 /// 527 /// `RELAXED` and `REG` are used purely for deserialization purposes. 528 pub(super) struct PublicKeyCredential<const RELAXED: bool, const REG: bool, AuthResp, Ext> { 529 /// [`rawId`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-rawid). 530 pub id: Option<CredentialId<Vec<u8>>>, 531 /// [`response`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-response). 532 pub response: AuthResp, 533 /// [`authenticatorAttachment`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-authenticatorattachment). 534 pub authenticator_attachment: AuthenticatorAttachment, 535 /// [`getClientExtensionResults`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-getclientextensionresults). 536 pub client_extension_results: Ext, 537 } 538 /// Deserializes the value for type. 539 pub(super) struct Type; 540 impl<'e> Deserialize<'e> for Type { 541 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 542 where 543 D: Deserializer<'e>, 544 { 545 /// `Visitor` for `Type`. 546 struct TypeVisitor; 547 impl Visitor<'_> for TypeVisitor { 548 type Value = Type; 549 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 550 formatter.write_str(PUBLIC_KEY) 551 } 552 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 553 where 554 E: Error, 555 { 556 if v == PUBLIC_KEY { 557 Ok(Type) 558 } else { 559 Err(E::invalid_value(Unexpected::Str(v), &PUBLIC_KEY)) 560 } 561 } 562 } 563 deserializer.deserialize_str(TypeVisitor) 564 } 565 } 566 /// `Visitor` for `PublicKeyCredential`. 567 /// 568 /// When `!RELAXED`, `REG` is ignored and all fields must exist and unknown fields are not allowed. 569 /// When `RELAXED`, unknown fields are ignored. 570 /// When `RELAXED` and `REG`, only `response` is required. 571 /// When `RELAXED` and `!REG`, only `id` and `response` are required. 572 struct PublicKeyCredentialVisitor<const RELAXED: bool, const REG: bool, R, E>( 573 pub PhantomData<fn() -> (R, E)>, 574 ); 575 impl<'d, const REL: bool, const REGI: bool, R, E> Visitor<'d> 576 for PublicKeyCredentialVisitor<REL, REGI, R, E> 577 where 578 R: Deserialize<'d>, 579 E: for<'a> Deserialize<'a> + ClientExtensions, 580 { 581 type Value = PublicKeyCredential<REL, REGI, R, E>; 582 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 583 formatter.write_str("PublicKeyCredential") 584 } 585 #[expect( 586 clippy::too_many_lines, 587 reason = "rather hide all the internal logic instead instead of moving into an outer scope" 588 )] 589 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 590 where 591 A: MapAccess<'d>, 592 { 593 /// `PublicKeyCredentialJSON` fields. 594 enum Field<const IGNORE_UNKNOWN: bool> { 595 /// `id`. 596 Id, 597 /// `type`. 598 Type, 599 /// `rawId`. 600 RawId, 601 /// `response`. 602 Response, 603 /// `authenticatorAttachment`. 604 AuthenticatorAttachment, 605 /// `clientExtensionResults`. 606 ClientExtensionResults, 607 /// Unknown field. 608 Other, 609 } 610 impl<'e, const IGNORE: bool> Deserialize<'e> for Field<IGNORE> { 611 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 612 where 613 D: Deserializer<'e>, 614 { 615 /// `Visitor` for `Field`. 616 struct FieldVisitor<const IGNORE_UNKNOWN: bool>; 617 impl<const IGN: bool> Visitor<'_> for FieldVisitor<IGN> { 618 type Value = Field<IGN>; 619 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 620 write!( 621 formatter, 622 "'{ID}', '{TYPE}', '{RAW_ID}', '{RESPONSE}', '{AUTHENTICATOR_ATTACHMENT}', or '{CLIENT_EXTENSION_RESULTS}'" 623 ) 624 } 625 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 626 where 627 E: Error, 628 { 629 match v { 630 ID => Ok(Field::Id), 631 TYPE => Ok(Field::Type), 632 RAW_ID => Ok(Field::RawId), 633 RESPONSE => Ok(Field::Response), 634 AUTHENTICATOR_ATTACHMENT => Ok(Field::AuthenticatorAttachment), 635 CLIENT_EXTENSION_RESULTS => Ok(Field::ClientExtensionResults), 636 _ => { 637 if IGN { 638 Ok(Field::Other) 639 } else { 640 Err(E::unknown_field(v, REG_FIELDS)) 641 } 642 } 643 } 644 } 645 } 646 deserializer.deserialize_identifier(FieldVisitor::<IGNORE>) 647 } 648 } 649 let mut opt_id = None; 650 let mut typ = false; 651 let mut raw = None; 652 let mut resp = None; 653 let mut attach = None; 654 let mut ext = None; 655 while let Some(key) = map.next_key::<Field<REL>>()? { 656 match key { 657 Field::Id => { 658 if opt_id.is_some() { 659 return Err(Error::duplicate_field(ID)); 660 } 661 opt_id = map.next_value::<CredentialId<_>>().map(Some)?; 662 } 663 Field::Type => { 664 if typ { 665 return Err(Error::duplicate_field(TYPE)); 666 } 667 typ = map.next_value::<Type>().map(|_| true)?; 668 } 669 Field::RawId => { 670 if raw.is_some() { 671 return Err(Error::duplicate_field(RAW_ID)); 672 } 673 raw = map.next_value::<CredentialId<_>>().map(Some)?; 674 } 675 Field::Response => { 676 if resp.is_some() { 677 return Err(Error::duplicate_field(RESPONSE)); 678 } 679 resp = map.next_value::<R>().map(Some)?; 680 } 681 Field::AuthenticatorAttachment => { 682 if attach.is_some() { 683 return Err(Error::duplicate_field(AUTHENTICATOR_ATTACHMENT)); 684 } 685 attach = map.next_value().map(Some)?; 686 } 687 Field::ClientExtensionResults => { 688 if ext.is_some() { 689 return Err(Error::duplicate_field(CLIENT_EXTENSION_RESULTS)); 690 } 691 ext = map.next_value::<Option<E>>().map(Some)?; 692 } 693 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?, 694 } 695 } 696 resp.ok_or_else(|| Error::missing_field(RESPONSE)) 697 .and_then(|response| { 698 opt_id.map_or_else( 699 || { 700 if REL && REGI { 701 Ok(None) 702 } else { 703 Err(Error::missing_field(ID)) 704 } 705 }, 706 |id| Ok(Some(id)), 707 ) 708 .and_then(|id| { 709 raw.map_or_else( 710 || { 711 if REL { 712 Ok(()) 713 } else { 714 Err(Error::missing_field(RAW_ID)) 715 } 716 }, 717 |raw_id| { 718 id.as_ref().map_or_else( 719 || Ok(()), 720 |i| { 721 if raw_id == i { 722 Ok(()) 723 } else { 724 Err(Error::invalid_value( 725 Unexpected::Bytes(raw_id.as_ref()), 726 &format!("{ID} and {RAW_ID} to match: {i:?}").as_str(), 727 )) 728 } 729 }, 730 ) 731 } 732 ) 733 .and_then(|()| { 734 ext.ok_or(false).and_then(|opt_ext| opt_ext.ok_or(true)).map_or_else( 735 |flag| { 736 if REL { 737 Ok(E::empty()) 738 } else if flag { 739 Err(Error::invalid_type(Unexpected::Other("null"), &format!("{CLIENT_EXTENSION_RESULTS} to be a map of allowed client extensions").as_str())) 740 } else { 741 Err(Error::missing_field(CLIENT_EXTENSION_RESULTS)) 742 } 743 }, 744 Ok 745 ) 746 .and_then(|client_extension_results| { 747 if typ || REL { 748 Ok(PublicKeyCredential { 749 id, 750 response, 751 authenticator_attachment: attach.flatten().unwrap_or(AuthenticatorAttachment::None), 752 client_extension_results, 753 }) 754 } else { 755 Err(Error::missing_field(TYPE)) 756 } 757 }) 758 }) 759 }) 760 }) 761 } 762 } 763 /// `"id"`. 764 const ID: &str = "id"; 765 /// `"type"`. 766 const TYPE: &str = "type"; 767 /// `"rawId"`. 768 const RAW_ID: &str = "rawId"; 769 /// `"response"`. 770 const RESPONSE: &str = "response"; 771 /// `"authenticatorAttachment"`. 772 const AUTHENTICATOR_ATTACHMENT: &str = "authenticatorAttachment"; 773 /// `"clientExtensionResults"`. 774 const CLIENT_EXTENSION_RESULTS: &str = "clientExtensionResults"; 775 /// `"public-key"`. 776 const PUBLIC_KEY: &str = "public-key"; 777 /// Fields for `PublicKeyCredentialJSON`. 778 const REG_FIELDS: &[&str; 6] = &[ 779 ID, 780 TYPE, 781 RAW_ID, 782 RESPONSE, 783 AUTHENTICATOR_ATTACHMENT, 784 CLIENT_EXTENSION_RESULTS, 785 ]; 786 impl<'de, const REL: bool, const REGI: bool, R, E> Deserialize<'de> 787 for PublicKeyCredential<REL, REGI, R, E> 788 where 789 R: Deserialize<'de>, 790 E: for<'a> Deserialize<'a> + ClientExtensions, 791 { 792 /// Deserializes a `struct` based on 793 /// [`PublicKeyCredentialJSON`](https://www.w3.org/TR/webauthn-3/#typedefdef-publickeycredentialjson). 794 /// 795 /// `REL` iff unknown fields should be ignored and not cause an error. 796 /// `REGI` iff `Self` is from a registration ceremony. 797 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 798 where 799 D: Deserializer<'de>, 800 { 801 deserializer.deserialize_struct( 802 "PublicKeyCredential", 803 REG_FIELDS, 804 PublicKeyCredentialVisitor::<REL, REGI, _, _>(PhantomData), 805 ) 806 } 807 } 808 impl Serialize for AllAcceptedCredentialsOptions<'_, '_> { 809 /// Serializes `self` to conform with 810 /// [`AllAcceptedCredentialsOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-allacceptedcredentialsoptions). 811 /// 812 /// # Examples 813 /// 814 /// ``` 815 /// # use core::str::FromStr; 816 /// # #[cfg(feature = "bin")] 817 /// # use webauthn_rp::bin::Decode; 818 /// # use webauthn_rp::{ 819 /// # request::{register::{UserHandle, USER_HANDLE_MIN_LEN}, AsciiDomain, RpId}, 820 /// # response::{error::CredentialIdErr, AllAcceptedCredentialsOptions, CredentialId}, 821 /// # }; 822 /// /// Retrieves the `CredentialId`s associated with `user_id` from the database. 823 /// # #[cfg(all(feature = "bin", feature = "custom"))] 824 /// fn get_credential_ids(user_id: UserHandle<&[u8]>) -> Result<Vec<CredentialId<Vec<u8>>>, CredentialIdErr> { 825 /// // ⋮ 826 /// # CredentialId::decode(vec![0; 16]).map(|cred_id| vec![cred_id]) 827 /// } 828 /// /// Retrieves the `UserHandle` from a session cookie. 829 /// # #[cfg(feature = "custom")] 830 /// fn get_user_handle() -> UserHandle<[u8; USER_HANDLE_MIN_LEN]> { 831 /// // ⋮ 832 /// # [0].into() 833 /// } 834 /// # #[cfg(feature = "custom")] 835 /// let user_id = get_user_handle(); 836 /// # #[cfg(all(feature = "bin", feature = "custom"))] 837 /// assert_eq!( 838 /// serde_json::to_string(&AllAcceptedCredentialsOptions { 839 /// rp_id: &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?), 840 /// user_id: (&user_id).into(), 841 /// all_accepted_credential_ids: get_credential_ids((&user_id).into())?, 842 /// }) 843 /// .unwrap(), 844 /// r#"{"rpId":"example.com","userId":"AA","allAcceptedCredentialIds":["AAAAAAAAAAAAAAAAAAAAAA"]}"# 845 /// ); 846 /// # Ok::<_, webauthn_rp::AggErr>(()) 847 /// ``` 848 #[inline] 849 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 850 where 851 S: Serializer, 852 { 853 serializer 854 .serialize_struct("AllAcceptedCredentialsOptions", 3) 855 .and_then(|mut ser| { 856 ser.serialize_field("rpId", self.rp_id).and_then(|()| { 857 ser.serialize_field("userId", &self.user_id).and_then(|()| { 858 ser.serialize_field( 859 "allAcceptedCredentialIds", 860 &self.all_accepted_credential_ids, 861 ) 862 .and_then(|()| ser.end()) 863 }) 864 }) 865 }) 866 } 867 } 868 impl Serialize for CurrentUserDetailsOptions<'_, '_, '_, '_> { 869 /// Serializes `self` to conform with 870 /// [`CurrentUserDetailsOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-currentuserdetailsoptions). 871 /// 872 /// # Examples 873 /// 874 /// ``` 875 /// # use core::str::FromStr; 876 /// # #[cfg(feature = "bin")] 877 /// # use webauthn_rp::bin::Decode; 878 /// # use webauthn_rp::{ 879 /// # request::{register::{Nickname, PublicKeyCredentialUserEntity, UserHandle, USER_HANDLE_MIN_LEN, Username}, AsciiDomain, RpId}, 880 /// # response::CurrentUserDetailsOptions, 881 /// # AggErr, 882 /// # }; 883 /// /// Retrieves the `PublicKeyCredentialUserEntity` info associated with `user_id` from the database. 884 /// # #[cfg(feature = "bin")] 885 /// fn get_user_info(user_id: UserHandle<&[u8]>) -> Result<(Username, Option<Nickname>), AggErr> { 886 /// // ⋮ 887 /// # Ok((Username::decode("foo").unwrap(), Some(Nickname::decode("foo").unwrap()))) 888 /// } 889 /// /// Retrieves the `UserHandle` from a session cookie. 890 /// # #[cfg(feature = "custom")] 891 /// fn get_user_handle() -> UserHandle<[u8; USER_HANDLE_MIN_LEN]> { 892 /// // ⋮ 893 /// # [0].into() 894 /// } 895 /// # #[cfg(feature = "custom")] 896 /// let user_handle = get_user_handle(); 897 /// # #[cfg(all(feature = "bin", feature = "custom"))] 898 /// let id = (&user_handle).into(); 899 /// # #[cfg(all(feature = "bin", feature = "custom"))] 900 /// let (name, display_name) = get_user_info(id)?; 901 /// # #[cfg(all(feature = "bin", feature = "custom"))] 902 /// assert_eq!( 903 /// serde_json::to_string(&CurrentUserDetailsOptions { 904 /// rp_id: &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?), 905 /// user: PublicKeyCredentialUserEntity { name, id, display_name, }, 906 /// }) 907 /// .unwrap(), 908 /// r#"{"rpId":"example.com","userId":"AA","name":"foo","displayName":"foo"}"# 909 /// ); 910 /// # Ok::<_, AggErr>(()) 911 /// ``` 912 #[inline] 913 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 914 where 915 S: Serializer, 916 { 917 serializer 918 .serialize_struct("CurrentUserDetailsOptions", 4) 919 .and_then(|mut ser| { 920 ser.serialize_field("rpId", self.rp_id).and_then(|()| { 921 ser.serialize_field("userId", &self.user.id).and_then(|()| { 922 ser.serialize_field("name", &self.user.name).and_then(|()| { 923 ser.serialize_field("displayName", &self.user.display_name) 924 .and_then(|()| ser.end()) 925 }) 926 }) 927 }) 928 }) 929 } 930 } 931 /// JSON `null`. 932 struct Null; 933 impl<'de> Deserialize<'de> for Null { 934 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 935 where 936 D: Deserializer<'de>, 937 { 938 /// `Visitor` for `Null`. 939 struct NullVisitor; 940 impl Visitor<'_> for NullVisitor { 941 type Value = Null; 942 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 943 formatter.write_str("null") 944 } 945 fn visit_none<E>(self) -> Result<Self::Value, E> 946 where 947 E: Error, 948 { 949 Ok(Null) 950 } 951 } 952 deserializer.deserialize_option(NullVisitor) 953 } 954 } 955 /// [`AuthenticationExtensionsPRFValues`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfvalues). 956 pub(super) struct AuthenticationExtensionsPrfValues; 957 /// `Visitor` for `AuthenticationExtensionsPrfValues`. 958 /// 959 /// Unknown fields are ignored iff `RELAXED`.`first` must always exist if `second` does. 960 /// `first` and `second` must be `null` if they exist. `first` must exist iff `!RELAXED`. 961 pub(super) struct AuthenticationExtensionsPrfValuesVisitor<const RELAXED: bool>; 962 impl<'d, const R: bool> Visitor<'d> for AuthenticationExtensionsPrfValuesVisitor<R> { 963 type Value = AuthenticationExtensionsPrfValues; 964 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 965 formatter.write_str("AuthenticationExtensionsPrfValues") 966 } 967 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 968 where 969 A: MapAccess<'d>, 970 { 971 /// Fields. 972 enum Field<const IGNORE_UNKNOWN: bool> { 973 /// `first` field. 974 First, 975 /// `second` field. 976 Second, 977 /// Unknown field. 978 Other, 979 } 980 impl<'e, const I: bool> Deserialize<'e> for Field<I> { 981 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 982 where 983 D: Deserializer<'e>, 984 { 985 /// `Visitor` for `Field`. 986 /// 987 /// Unknown fields are ignored iff `IGNORE_UNKNOWN`. 988 struct FieldVisitor<const IGNORE_UNKNOWN: bool>; 989 impl<const IG: bool> Visitor<'_> for FieldVisitor<IG> { 990 type Value = Field<IG>; 991 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 992 write!(formatter, "'{FIRST}' or '{SECOND}'") 993 } 994 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 995 where 996 E: Error, 997 { 998 match v { 999 FIRST => Ok(Field::First), 1000 SECOND => Ok(Field::Second), 1001 _ => { 1002 if IG { 1003 Ok(Field::Other) 1004 } else { 1005 Err(E::unknown_field(v, PRF_VALUES_FIELDS)) 1006 } 1007 } 1008 } 1009 } 1010 } 1011 deserializer.deserialize_identifier(FieldVisitor) 1012 } 1013 } 1014 let mut first = None; 1015 let mut second = None; 1016 while let Some(key) = map.next_key::<Field<R>>()? { 1017 match key { 1018 Field::First => { 1019 if first.is_some() { 1020 return Err(Error::duplicate_field(FIRST)); 1021 } 1022 first = map.next_value::<Null>().map(Some)?; 1023 } 1024 Field::Second => { 1025 if second.is_some() { 1026 return Err(Error::duplicate_field(SECOND)); 1027 } 1028 second = map.next_value::<Null>().map(Some)?; 1029 } 1030 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?, 1031 } 1032 } 1033 if first.is_some() || (R && second.is_none()) { 1034 Ok(AuthenticationExtensionsPrfValues) 1035 } else { 1036 Err(Error::missing_field(FIRST)) 1037 } 1038 } 1039 } 1040 /// `"first"` 1041 const FIRST: &str = "first"; 1042 /// `"second"` 1043 const SECOND: &str = "second"; 1044 /// `AuthenticationExtensionsPrfValues` fields. 1045 pub(super) const PRF_VALUES_FIELDS: &[&str; 2] = &[FIRST, SECOND]; 1046 impl<'de> Deserialize<'de> for AuthenticationExtensionsPrfValues { 1047 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1048 where 1049 D: Deserializer<'de>, 1050 { 1051 deserializer.deserialize_struct( 1052 "AuthenticationExtensionsPrfValues", 1053 PRF_VALUES_FIELDS, 1054 AuthenticationExtensionsPrfValuesVisitor::<false>, 1055 ) 1056 } 1057 } 1058 /// [`AuthenticationExtensionsPRFOutputs`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfoutputs). 1059 /// 1060 /// `RELAXED` iff unknown fields are ignored. 1061 /// 1062 /// `REGISTRATION` iff 1063 /// [`enabled`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-enabled) 1064 /// is required (and must not be `null`); otherwise it's forbidden. 1065 /// 1066 /// The contained `Option` is `Some` iff `REGISTRATION`. 1067 pub(super) struct AuthenticationExtensionsPrfOutputsHelper< 1068 const RELAXED: bool, 1069 const REGISTRATION: bool, 1070 Prf, 1071 >(pub Option<bool>, pub PhantomData<fn() -> Prf>); 1072 /// `Visitor` for `AuthenticationExtensionsPrfOutputs`. 1073 /// 1074 /// Unknown fields are ignored iff `RELAXED`.`enabled` must exist and not be `null` iff `REGISTRATION`. 1075 struct AuthenticationExtensionsPrfOutputsVisitor<const RELAXED: bool, const REGISTRATION: bool, Prf>( 1076 PhantomData<fn() -> Prf>, 1077 ); 1078 impl<'d, const REL: bool, const REG: bool, Prf> Visitor<'d> 1079 for AuthenticationExtensionsPrfOutputsVisitor<REL, REG, Prf> 1080 where 1081 Prf: for<'a> Deserialize<'a>, 1082 { 1083 type Value = AuthenticationExtensionsPrfOutputsHelper<REL, REG, Prf>; 1084 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 1085 formatter.write_str("AuthenticationExtensionsPrfOutputs") 1086 } 1087 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 1088 where 1089 A: MapAccess<'d>, 1090 { 1091 /// Fields. 1092 enum Field<const IGNORE_UNKNOWN: bool, const REGI: bool> { 1093 /// `enabled` field. 1094 Enabled, 1095 /// `results` field. 1096 Results, 1097 /// Unknown field. 1098 Other, 1099 } 1100 impl<'e, const I: bool, const R: bool> Deserialize<'e> for Field<I, R> { 1101 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1102 where 1103 D: Deserializer<'e>, 1104 { 1105 /// `Visitor` for `Field`. 1106 /// 1107 /// Unknown fields are ignored iff `IGNORE_UNKNOWN`. 1108 /// `enabled` is allowed to exist iff `REGI`. 1109 struct FieldVisitor<const IGNORE_UNKNOWN: bool, const REGI: bool>; 1110 impl<const IG: bool, const RE: bool> Visitor<'_> for FieldVisitor<IG, RE> { 1111 type Value = Field<IG, RE>; 1112 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 1113 if RE { 1114 write!(formatter, "'{ENABLED}' or '{RESULTS}'") 1115 } else { 1116 write!(formatter, "'{RESULTS}'") 1117 } 1118 } 1119 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 1120 where 1121 E: Error, 1122 { 1123 match v { 1124 ENABLED => { 1125 if RE { 1126 Ok(Field::Enabled) 1127 } else { 1128 Err(E::unknown_field(v, PRF_AUTH_OUTPUTS_FIELDS)) 1129 } 1130 } 1131 RESULTS => Ok(Field::Results), 1132 _ => { 1133 if IG { 1134 Ok(Field::Other) 1135 } else if RE { 1136 Err(E::unknown_field(v, PRF_REG_OUTPUTS_FIELDS)) 1137 } else { 1138 Err(E::unknown_field(v, PRF_AUTH_OUTPUTS_FIELDS)) 1139 } 1140 } 1141 } 1142 } 1143 } 1144 deserializer.deserialize_identifier(FieldVisitor) 1145 } 1146 } 1147 let mut enabled = None; 1148 let mut results = None; 1149 while let Some(key) = map.next_key::<Field<REL, REG>>()? { 1150 match key { 1151 Field::Enabled => { 1152 if enabled.is_some() { 1153 return Err(Error::duplicate_field(ENABLED)); 1154 } 1155 enabled = map.next_value().map(Some)?; 1156 } 1157 Field::Results => { 1158 if results.is_some() { 1159 return Err(Error::duplicate_field(RESULTS)); 1160 } 1161 results = map.next_value::<Option<Prf>>().map(Some)?; 1162 } 1163 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?, 1164 } 1165 } 1166 if REG { 1167 enabled.ok_or_else(|| Error::missing_field(ENABLED)).and_then(|e| { 1168 if e || results.is_none() { 1169 Ok(()) 1170 } else { 1171 Err(Error::custom("prf must not have 'results', including a null 'results', if 'enabled' is false")) 1172 } 1173 }) 1174 } else { 1175 Ok(()) 1176 }.map(|()| AuthenticationExtensionsPrfOutputsHelper(enabled, PhantomData)) 1177 } 1178 } 1179 /// `"enabled"` 1180 const ENABLED: &str = "enabled"; 1181 /// `"results"` 1182 const RESULTS: &str = "results"; 1183 /// `AuthenticationExtensionsPrfOutputs` field during registration. 1184 const PRF_REG_OUTPUTS_FIELDS: &[&str; 2] = &[ENABLED, RESULTS]; 1185 /// `AuthenticationExtensionsPrfOutputs` field during authentication. 1186 const PRF_AUTH_OUTPUTS_FIELDS: &[&str; 1] = &[RESULTS]; 1187 impl<'de, const REL: bool, const REG: bool, Prf> Deserialize<'de> 1188 for AuthenticationExtensionsPrfOutputsHelper<REL, REG, Prf> 1189 where 1190 for<'a> Prf: Deserialize<'a>, 1191 { 1192 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1193 where 1194 D: Deserializer<'de>, 1195 { 1196 deserializer.deserialize_struct( 1197 "AuthenticationExtensionsPrfOutputs", 1198 if REG { 1199 PRF_REG_OUTPUTS_FIELDS 1200 } else { 1201 PRF_AUTH_OUTPUTS_FIELDS 1202 }, 1203 AuthenticationExtensionsPrfOutputsVisitor::<REL, REG, Prf>(PhantomData), 1204 ) 1205 } 1206 }