webauthn_rp

WebAuthn RP library.
git clone https://git.philomathiclife.com/repos/webauthn_rp
Log | Files | Refs | README

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 }