webauthn_rp

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

ser.rs (36885B)


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