webauthn_rp

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

ser.rs (50100B)


      1 #[cfg(test)]
      2 mod tests;
      3 use super::{
      4     super::{super::response::ser::Null, ser::PrfHelper},
      5     AllowedCredential, AllowedCredentials, Challenge, CredentialMediationRequirement,
      6     CredentialUiMode, Credentials as _, DiscoverableAuthenticationClientState,
      7     DiscoverableCredentialRequestOptions, Extension, ExtensionReq, FIVE_MINUTES, Hints,
      8     NonDiscoverableAuthenticationClientState, NonDiscoverableCredentialRequestOptions, PrfInput,
      9     PrfInputOwned, PublicKeyCredentialRequestOptions, RpId, UserVerificationRequirement,
     10 };
     11 use core::{
     12     error::Error as E,
     13     fmt::{self, Display, Formatter},
     14     num::NonZeroU32,
     15 };
     16 use serde::{
     17     de::{Deserialize, Deserializer, Error, MapAccess, Unexpected, Visitor},
     18     ser::{Serialize, SerializeMap as _, SerializeStruct as _, Serializer},
     19 };
     20 /// `"immediate"`.
     21 const IMMEDIATE: &str = "immediate";
     22 impl Serialize for CredentialUiMode {
     23     /// Serializes `self` as a [`prim@str`] conforming with
     24     /// [`CredentialUiMode`](https://www.w3.org/TR/credential-management-1/#enumdef-credentialuimode).
     25     ///
     26     /// # Examples
     27     ///
     28     /// ```
     29     /// # use webauthn_rp::request::auth::CredentialUiMode;
     30     /// assert_eq!(
     31     ///     serde_json::to_string(&CredentialUiMode::Immediate)?,
     32     ///     r#""immediate""#
     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(IMMEDIATE)
     42     }
     43 }
     44 impl Serialize for PrfInputOwned {
     45     /// See [`PrfInput::serialize`]
     46     #[inline]
     47     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     48     where
     49         S: Serializer,
     50     {
     51         PrfInput {
     52             first: self.first.as_slice(),
     53             second: self.second.as_deref(),
     54         }
     55         .serialize(serializer)
     56     }
     57 }
     58 impl Serialize for AllowedCredential {
     59     /// Serializes `self` to conform with
     60     /// [`PublicKeyCredentialDescriptorJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialdescriptorjson).
     61     ///
     62     /// # Examples
     63     ///
     64     /// ```
     65     /// # #[cfg(all(feature = "bin", feature = "custom"))]
     66     /// # use webauthn_rp::{bin::Decode, response::bin::DecodeAuthTransportsErr};
     67     /// # use webauthn_rp::{
     68     /// #     request::{auth::AllowedCredential, PublicKeyCredentialDescriptor},
     69     /// #     response::{AuthTransports, CredentialId},
     70     /// # };
     71     /// /// Retrieves the `AuthTransports` associated with the unique `cred_id`
     72     /// /// from the database.
     73     /// # #[cfg(all(feature = "bin", feature = "custom"))]
     74     /// fn get_transports(cred_id: CredentialId<&[u8]>) -> Result<AuthTransports, DecodeAuthTransportsErr> {
     75     ///     // ⋮
     76     /// #     AuthTransports::decode(32)
     77     /// }
     78     /// // `CredentialId::try_from` only exists when `custom` is enabled; and even then, it is
     79     /// // likely never needed since the `CredentialId` was originally sent from the client and is likely
     80     /// // stored in a database which would be fetched by `UserHandle` or `Authentication::raw_id`.
     81     /// # #[cfg(all(feature = "bin", feature = "custom"))]
     82     /// let id = CredentialId::try_from(vec![0; 16].into_boxed_slice())?;
     83     /// # #[cfg(all(feature = "bin", feature = "custom"))]
     84     /// let transports = get_transports((&id).into())?;
     85     /// # #[cfg(all(feature = "bin", feature = "custom"))]
     86     /// assert_eq!(
     87     ///     serde_json::to_string(&AllowedCredential::from(PublicKeyCredentialDescriptor {
     88     ///         id,
     89     ///         transports
     90     ///     })).unwrap(),
     91     ///     r#"{"type":"public-key","id":"AAAAAAAAAAAAAAAAAAAAAA","transports":["usb"]}"#
     92     /// );
     93     /// # Ok::<_, webauthn_rp::AggErr>(())
     94     /// ```
     95     #[inline]
     96     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     97     where
     98         S: Serializer,
     99     {
    100         self.credential.serialize(serializer)
    101     }
    102 }
    103 impl Serialize for AllowedCredentials {
    104     /// Serializes `self` to conform with
    105     /// [`allowCredentials`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptionsjson-allowcredentials).
    106     ///
    107     /// # Examples
    108     ///
    109     /// ```
    110     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    111     /// # use webauthn_rp::{bin::Decode, response::bin::DecodeAuthTransportsErr};
    112     /// # use webauthn_rp::{
    113     /// #     request::{auth::AllowedCredentials, PublicKeyCredentialDescriptor, Credentials},
    114     /// #     response::{AuthTransports, CredentialId},
    115     /// # };
    116     /// /// Retrieves the `AuthTransports` associated with the unique `cred_id`
    117     /// /// from the database.
    118     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    119     /// fn get_transports(cred_id: CredentialId<&[u8]>) -> Result<AuthTransports, DecodeAuthTransportsErr> {
    120     ///     // ⋮
    121     /// #     AuthTransports::decode(32)
    122     /// }
    123     /// // `CredentialId::try_from` only exists when `custom` is enabled; and even then, it is
    124     /// // likely never needed since the `CredentialId` was originally sent from the client and is likely
    125     /// // stored in a database which would be fetched by `UserHandle` or `Authentication::raw_id`.
    126     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    127     /// let id = CredentialId::try_from(vec![0; 16].into_boxed_slice())?;
    128     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    129     /// let transports = get_transports((&id).into())?;
    130     /// let mut creds = AllowedCredentials::with_capacity(1);
    131     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    132     /// creds.push(PublicKeyCredentialDescriptor { id, transports }.into());
    133     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    134     /// assert_eq!(
    135     ///     serde_json::to_string(&creds).unwrap(),
    136     ///     r#"[{"type":"public-key","id":"AAAAAAAAAAAAAAAAAAAAAA","transports":["usb"]}]"#
    137     /// );
    138     /// # Ok::<_, webauthn_rp::AggErr>(())
    139     /// ```
    140     #[inline]
    141     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    142     where
    143         S: Serializer,
    144     {
    145         self.creds.serialize(serializer)
    146     }
    147 }
    148 /// [`evalByCredential`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfinputs-evalbycredential).
    149 struct PrfCreds<'a>(&'a AllowedCredentials);
    150 impl Serialize for PrfCreds<'_> {
    151     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    152     where
    153         S: Serializer,
    154     {
    155         serializer
    156             .serialize_map(Some(self.0.prf_count))
    157             .and_then(|mut ser| {
    158                 self.0
    159                     .creds
    160                     .iter()
    161                     .try_fold((), |(), cred| {
    162                         cred.extension.prf.as_ref().map_or(Ok(()), |input| {
    163                             ser.serialize_entry(&cred.credential.id, input)
    164                         })
    165                     })
    166                     .and_then(|()| ser.end())
    167             })
    168     }
    169 }
    170 /// [`AuthenticationExtensionsPRFInputs`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfinputs).
    171 struct PrfInputs<'a, 'b, 'c> {
    172     /// [`eval`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfinputs-eval).
    173     eval: Option<PrfInput<'a, 'b>>,
    174     /// [`evalByCredential`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfinputs-evalbycredential).
    175     eval_by_credential: PrfCreds<'c>,
    176 }
    177 impl Serialize for PrfInputs<'_, '_, '_> {
    178     #[expect(
    179         clippy::arithmetic_side_effects,
    180         reason = "comment explains how overflow is not possible"
    181     )]
    182     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    183     where
    184         S: Serializer,
    185     {
    186         serializer
    187             .serialize_struct(
    188                 "PrfInputs",
    189                 // The max is 1 + 1 = 2, so overflow is not an issue.
    190                 usize::from(self.eval.is_some())
    191                     + usize::from(self.eval_by_credential.0.prf_count > 0),
    192             )
    193             .and_then(|mut ser| {
    194                 self.eval
    195                     .map_or(Ok(()), |eval| ser.serialize_field("eval", &eval))
    196                     .and_then(|()| {
    197                         if self.eval_by_credential.0.prf_count == 0 {
    198                             Ok(())
    199                         } else {
    200                             ser.serialize_field("evalByCredential", &self.eval_by_credential)
    201                         }
    202                     })
    203                     .and_then(|()| ser.end())
    204             })
    205     }
    206 }
    207 /// Serializes `self` to conform with
    208 /// [`AuthenticationExtensionsClientInputsJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsclientinputsjson).
    209 struct ExtensionHelper<'a, 'b, 'c> {
    210     /// [`extension`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptionsjson-extensions).
    211     extension: &'a Extension<'b, 'c>,
    212     /// [`extension`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptionsjson-extensions).
    213     ///
    214     /// Some extensions contain records, so we need both this and above.
    215     allow_credentials: &'a AllowedCredentials,
    216 }
    217 impl Serialize for ExtensionHelper<'_, '_, '_> {
    218     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    219     where
    220         S: Serializer,
    221     {
    222         let ext_count =
    223             usize::from(self.extension.prf.is_some() || self.allow_credentials.prf_count > 0);
    224         serializer
    225             .serialize_struct("Extension", ext_count)
    226             .and_then(|mut ser| {
    227                 if ext_count == 0 {
    228                     Ok(())
    229                 } else {
    230                     ser.serialize_field(
    231                         "prf",
    232                         &PrfInputs {
    233                             eval: self.extension.prf.map(|prf| prf.0),
    234                             eval_by_credential: PrfCreds(self.allow_credentials),
    235                         },
    236                     )
    237                 }
    238                 .and_then(|()| ser.end())
    239             })
    240     }
    241 }
    242 /// `"challenge"`
    243 const CHALLENGE: &str = "challenge";
    244 /// `"timeout"`
    245 const TIMEOUT: &str = "timeout";
    246 /// `"rpId"`
    247 const RP_ID: &str = "rpId";
    248 /// `"allowCredentials"`
    249 const ALLOW_CREDENTIALS: &str = "allowCredentials";
    250 /// `"extensions"`
    251 const EXTENSIONS: &str = "extensions";
    252 /// `"hints"`
    253 const HINTS: &str = "hints";
    254 /// `"userVerification"`
    255 const USER_VERIFICATION: &str = "userVerification";
    256 /// Helper type that peforms the serialization for both [`DiscoverableAuthenticationClientState`] and
    257 /// [`NonDiscoverableAuthenticationClientState`] and
    258 struct AuthenticationClientState<'rp_id, 'prf_first, 'prf_second, 'opt, 'cred>(
    259     &'opt PublicKeyCredentialRequestOptions<'rp_id, 'prf_first, 'prf_second>,
    260     &'cred AllowedCredentials,
    261 );
    262 impl Serialize for AuthenticationClientState<'_, '_, '_, '_, '_> {
    263     #[inline]
    264     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    265     where
    266         S: Serializer,
    267     {
    268         serializer
    269             .serialize_struct("PublicKeyCredentialRequestOptions", 7)
    270             .and_then(|mut ser| {
    271                 ser.serialize_field(CHALLENGE, &self.0.challenge)
    272                     .and_then(|()| {
    273                         ser.serialize_field(TIMEOUT, &self.0.timeout)
    274                             .and_then(|()| {
    275                                 ser.serialize_field(RP_ID, &self.0.rp_id).and_then(|()| {
    276                                     ser.serialize_field(ALLOW_CREDENTIALS, &self.1)
    277                                         .and_then(|()| {
    278                                             ser.serialize_field(
    279                                                 USER_VERIFICATION,
    280                                                 &self.0.user_verification,
    281                                             )
    282                                             .and_then(
    283                                                 |()| {
    284                                                     ser.serialize_field(HINTS, &self.0.hints)
    285                                                         .and_then(|()| {
    286                                                             ser.serialize_field(
    287                                                                 EXTENSIONS,
    288                                                                 &ExtensionHelper {
    289                                                                     extension: &self.0.extensions,
    290                                                                     allow_credentials: self.1,
    291                                                                 },
    292                                                             )
    293                                                             .and_then(|()| ser.end())
    294                                                         })
    295                                                 },
    296                                             )
    297                                         })
    298                                 })
    299                             })
    300                     })
    301             })
    302     }
    303 }
    304 /// `"mediation"`.
    305 const MEDIATION: &str = "mediation";
    306 /// `"uiMode"`.
    307 const UI_MODE: &str = "uiMode";
    308 /// `"publicKey"`.
    309 const PUBLIC_KEY: &str = "publicKey";
    310 impl Serialize for DiscoverableCredentialRequestOptions<'_, '_, '_> {
    311     /// Serializes `self` to conform with
    312     /// [`CredentialRequestOptions`](https://www.w3.org/TR/credential-management-1/#dictdef-credentialrequestoptions).
    313     ///
    314     /// Note [`signal`](https://www.w3.org/TR/credential-management-1/#dom-credentialrequestoptions-signal)
    315     /// is not present, and [`publicKey`](https://www.w3.org/TR/credential-management-1/#sctn-cred-type-registry)
    316     /// is serialized to conform to
    317     /// [`PublicKeyCredentialRequestOptionsJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialrequestoptionsjson).
    318     /// Additionally [`uiMode`](https://www.w3.org/TR/credential-management-1/#dom-credentialrequestoptions-uiMode)
    319     /// is not present iff [`Self::ui_mode`] is `None`.
    320     #[inline]
    321     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    322     where
    323         S: Serializer,
    324     {
    325         serializer
    326             .serialize_struct(
    327                 "DiscoverableCredentialRequestOptions",
    328                 if self.ui_mode.is_none() { 2 } else { 3 },
    329             )
    330             .and_then(|mut ser| {
    331                 ser.serialize_field(MEDIATION, &self.mediation)
    332                     .and_then(|()| {
    333                         if self.ui_mode.is_none() {
    334                             Ok(())
    335                         } else {
    336                             ser.serialize_field(UI_MODE, &self.ui_mode)
    337                         }
    338                         .and_then(|()| {
    339                             ser.serialize_field(
    340                                 PUBLIC_KEY,
    341                                 &AuthenticationClientState(
    342                                     &self.public_key,
    343                                     &AllowedCredentials::with_capacity(0),
    344                                 ),
    345                             )
    346                             .and_then(|()| ser.end())
    347                         })
    348                     })
    349             })
    350     }
    351 }
    352 impl Serialize for NonDiscoverableCredentialRequestOptions<'_, '_, '_> {
    353     /// Serializes `self` to conform with
    354     /// [`CredentialRequestOptions`](https://www.w3.org/TR/credential-management-1/#dictdef-credentialrequestoptions).
    355     ///
    356     /// Note [`signal`](https://www.w3.org/TR/credential-management-1/#dom-credentialrequestoptions-signal)
    357     /// and [`uiMode`](https://www.w3.org/TR/credential-management-1/#dom-credentialrequestoptions-uiMode)
    358     /// are not present, and [`publicKey`](https://www.w3.org/TR/credential-management-1/#sctn-cred-type-registry)
    359     /// is serialized to conform to
    360     /// [`PublicKeyCredentialRequestOptionsJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialrequestoptionsjson).
    361     #[inline]
    362     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    363     where
    364         S: Serializer,
    365     {
    366         serializer
    367             .serialize_struct("NonDiscoverableCredentialRequestOptions", 2)
    368             .and_then(|mut ser| {
    369                 ser.serialize_field(MEDIATION, &self.mediation)
    370                     .and_then(|()| {
    371                         ser.serialize_field(
    372                             PUBLIC_KEY,
    373                             &AuthenticationClientState(&self.options, &self.allow_credentials),
    374                         )
    375                         .and_then(|()| ser.end())
    376                     })
    377             })
    378     }
    379 }
    380 impl Serialize for DiscoverableAuthenticationClientState<'_, '_, '_> {
    381     /// Serializes `self` according to [`DiscoverableCredentialRequestOptions::serialize`].
    382     ///
    383     /// # Examples
    384     ///
    385     /// ```
    386     /// # use webauthn_rp::{
    387     /// #     request::{
    388     /// #         auth::{
    389     /// #             AllowedCredential, AllowedCredentials, CredentialSpecificExtension, Extension,
    390     /// #             PrfInputOwned, DiscoverableCredentialRequestOptions
    391     /// #         },
    392     /// #         AsciiDomain, ExtensionReq, Hints, PublicKeyCredentialHint, PrfInput, RpId, PublicKeyCredentialDescriptor, Credentials, UserVerificationRequirement,
    393     /// #     },
    394     /// #     response::{AuthTransports, CredentialId},
    395     /// # };
    396     /// let rp_id = RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?);
    397     /// let mut options = DiscoverableCredentialRequestOptions::passkey(&rp_id);
    398     /// options.public_key.hints = Hints::EMPTY.add(PublicKeyCredentialHint::SecurityKey);
    399     /// options.public_key.extensions = Extension {
    400     ///     prf: Some((PrfInput {
    401     ///         first: [0; 4].as_slice(),
    402     ///         second: None,
    403     ///     }, ExtensionReq::Require)),
    404     /// };
    405     /// let client_state = serde_json::to_string(&options.start_ceremony()?.1).unwrap();
    406     /// let json = serde_json::json!({
    407     ///     "mediation":"required",
    408     ///     "publicKey":{
    409     ///         "challenge":"AAAAAAAAAAAAAAAAAAAAAA",
    410     ///         "timeout":300000,
    411     ///         "rpId":"example.com",
    412     ///         "allowCredentials":[],
    413     ///         "userVerification":"required",
    414     ///         "hints":[
    415     ///             "security-key"
    416     ///         ],
    417     ///         "extensions":{
    418     ///             "prf":{
    419     ///                 "eval":{
    420     ///                     "first":"AAAAAA"
    421     ///                 },
    422     ///             }
    423     ///         }
    424     ///     }
    425     /// }).to_string();
    426     /// // Since `Challenge`s are randomly generated, we don't know what it will be; thus
    427     /// // we test the JSON string for everything except it.
    428     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    429     /// assert_eq!(client_state.get(..50), json.get(..50));
    430     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    431     /// assert_eq!(client_state.get(72..), json.get(72..));
    432     /// # Ok::<_, webauthn_rp::AggErr>(())
    433     /// ```
    434     #[inline]
    435     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    436     where
    437         S: Serializer,
    438     {
    439         self.0.serialize(serializer)
    440     }
    441 }
    442 impl Serialize for NonDiscoverableAuthenticationClientState<'_, '_, '_> {
    443     /// Serializes `self` according to [`NonDiscoverableCredentialRequestOptions::serialize`].
    444     ///
    445     /// # Examples
    446     ///
    447     /// ```
    448     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    449     /// # use webauthn_rp::{bin::Decode, response::bin::DecodeAuthTransportsErr};
    450     /// # use webauthn_rp::{
    451     /// #     request::{
    452     /// #         auth::{
    453     /// #             AllowedCredential, AllowedCredentials, CredentialSpecificExtension, Extension,
    454     /// #             PrfInputOwned, NonDiscoverableCredentialRequestOptions
    455     /// #         },
    456     /// #         AsciiDomain, ExtensionReq, Hints, PublicKeyCredentialHint, PrfInput, RpId, PublicKeyCredentialDescriptor, Credentials, UserVerificationRequirement,
    457     /// #     },
    458     /// #     response::{AuthTransports, CredentialId},
    459     /// # };
    460     /// /// Retrieves the `AuthTransports` associated with the unique `cred_id`
    461     /// /// from the database.
    462     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    463     /// fn get_transports(cred_id: CredentialId<&[u8]>) -> Result<AuthTransports, DecodeAuthTransportsErr> {
    464     ///     // ⋮
    465     /// #     AuthTransports::decode(32)
    466     /// }
    467     /// let mut creds = AllowedCredentials::with_capacity(1);
    468     /// // `CredentialId::try_from` only exists when `custom` is enabled; and even then, it is
    469     /// // likely never needed since the `CredentialId` was originally sent from the client and is likely
    470     /// // stored in a database which would be fetched by `UserHandle` or `Authentication::raw_id`.
    471     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    472     /// let id = CredentialId::try_from(vec![0; 16].into_boxed_slice())?;
    473     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    474     /// let transports = get_transports((&id).into())?;
    475     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    476     /// creds.push(AllowedCredential {
    477     ///     credential: PublicKeyCredentialDescriptor { id, transports },
    478     ///     extension: CredentialSpecificExtension {
    479     ///         prf: Some(PrfInputOwned {
    480     ///             first: vec![2; 6],
    481     ///             second: Some(vec![3; 2]),
    482     ///             ext_req: ExtensionReq::Require,
    483     ///         }),
    484     ///     },
    485     /// });
    486     /// let rp_id = RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?);
    487     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    488     /// let mut options = NonDiscoverableCredentialRequestOptions::second_factor(&rp_id, creds);
    489     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    490     /// let opts = &mut options.options;
    491     /// # #[cfg(not(all(feature = "bin", feature = "custom")))]
    492     /// # let mut opts = webauthn_rp::DiscoverableCredentialRequestOptions::passkey(&rp_id).public_key;
    493     /// opts.hints = Hints::EMPTY.add(PublicKeyCredentialHint::SecurityKey);
    494     /// // This is actually useless since `CredentialSpecificExtension` takes priority
    495     /// // when the client receives the payload. We set it for illustration purposes only.
    496     /// // If `creds` contained an `AllowedCredential` that didn't set
    497     /// // `CredentialSpecificExtension::prf`, then this would be used for it.
    498     /// opts.extensions = Extension {
    499     ///     prf: Some((PrfInput {
    500     ///         first: [0; 4].as_slice(),
    501     ///         second: None,
    502     ///     }, ExtensionReq::Require)),
    503     /// };
    504     /// // Since we are requesting the PRF extension, we must require user verification; otherwise
    505     /// // `NonDiscoverableCredentialRequestOptions::start_ceremony` would error.
    506     /// opts.user_verification = UserVerificationRequirement::Required;
    507     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    508     /// let client_state = serde_json::to_string(&options.start_ceremony()?.1).unwrap();
    509     /// let json = serde_json::json!({
    510     ///     "mediation":"required",
    511     ///     "publicKey":{
    512     ///         "challenge":"AAAAAAAAAAAAAAAAAAAAAA",
    513     ///         "timeout":300000,
    514     ///         "rpId":"example.com",
    515     ///         "allowCredentials":[
    516     ///             {
    517     ///                 "type":"public-key",
    518     ///                 "id":"AAAAAAAAAAAAAAAAAAAAAA",
    519     ///                 "transports":["usb"]
    520     ///             }
    521     ///         ],
    522     ///         "userVerification":"required",
    523     ///         "hints":[
    524     ///             "security-key"
    525     ///         ],
    526     ///         "extensions":{
    527     ///             "prf":{
    528     ///                 "eval":{
    529     ///                     "first":"AAAAAA"
    530     ///                 },
    531     ///                 "evalByCredential":{
    532     ///                     "AAAAAAAAAAAAAAAAAAAAAA":{
    533     ///                         "first":"AgICAgIC",
    534     ///                         "second":"AwM"
    535     ///                     }
    536     ///                 }
    537     ///             }
    538     ///         }
    539     ///     }
    540     /// }).to_string();
    541     /// // Since `Challenge`s are randomly generated, we don't know what it will be; thus
    542     /// // we test the JSON string for everything except it.
    543     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    544     /// assert_eq!(client_state.get(..50), json.get(..50));
    545     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    546     /// assert_eq!(client_state.get(72..), json.get(72..));
    547     /// # Ok::<_, webauthn_rp::AggErr>(())
    548     /// ```
    549     #[inline]
    550     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    551     where
    552         S: Serializer,
    553     {
    554         self.0.serialize(serializer)
    555     }
    556 }
    557 impl<'de> Deserialize<'de> for CredentialUiMode {
    558     /// Deserializes a [`prim@str`] based on
    559     /// [`CredentialUiMode`](https://www.w3.org/TR/credential-management-1/#enumdef-credentialuimode).
    560     ///
    561     /// # Examples
    562     ///
    563     /// ```
    564     /// # use webauthn_rp::request::auth::CredentialUiMode;
    565     /// assert_eq!(
    566     ///     serde_json::from_str::<CredentialUiMode>(r#""immediate""#)?,
    567     ///     CredentialUiMode::Immediate,
    568     /// );
    569     /// # Ok::<_, serde_json::Error>(())
    570     /// ```
    571     #[inline]
    572     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    573     where
    574         D: Deserializer<'de>,
    575     {
    576         struct CredentialUiModeVisitor;
    577         impl Visitor<'_> for CredentialUiModeVisitor {
    578             type Value = CredentialUiMode;
    579             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    580                 write!(formatter, "'{IMMEDIATE}'")
    581             }
    582             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    583             where
    584                 E: Error,
    585             {
    586                 if v == IMMEDIATE {
    587                     Ok(CredentialUiMode::Immediate)
    588                 } else {
    589                     Err(E::invalid_value(
    590                         Unexpected::Str(v),
    591                         &format!("'{IMMEDIATE}'").as_str(),
    592                     ))
    593                 }
    594             }
    595         }
    596         deserializer.deserialize_str(CredentialUiModeVisitor)
    597     }
    598 }
    599 /// Similar to [`Extension`] except [`PrfInputOwned`] is used.
    600 ///
    601 /// This is primarily useful to assist [`ClientCredentialRequestOptions::deserialize`].
    602 #[derive(Debug, Default)]
    603 pub struct ExtensionOwned {
    604     /// See [`Extension::prf`].
    605     pub prf: Option<PrfInputOwned>,
    606 }
    607 impl ExtensionOwned {
    608     /// Returns an `Extension` based on `self`.
    609     #[inline]
    610     #[must_use]
    611     pub fn as_extension(&self) -> Extension<'_, '_> {
    612         Extension {
    613             prf: self.prf.as_ref().map(|prf| {
    614                 (
    615                     PrfInput {
    616                         first: &prf.first,
    617                         second: prf.second.as_deref(),
    618                     },
    619                     prf.ext_req,
    620                 )
    621             }),
    622         }
    623     }
    624     /// Returns an `Extension` based on `self` and `prf`.
    625     ///
    626     /// Note `prf` is used _unconditionally_ regardless if [`Self::prf`] is `Some`.
    627     #[inline]
    628     #[must_use]
    629     pub const fn with_prf<'prf_first, 'prf_second>(
    630         &self,
    631         prf: (PrfInput<'prf_first, 'prf_second>, ExtensionReq),
    632     ) -> Extension<'prf_first, 'prf_second> {
    633         Extension { prf: Some(prf) }
    634     }
    635 }
    636 impl<'de> Deserialize<'de> for ExtensionOwned {
    637     /// Deserializes a `struct` according to the following pseudo-schema:
    638     ///
    639     /// ```json
    640     /// {
    641     ///   "prf": null | PRFJSON
    642     /// }
    643     /// // PRFJSON:
    644     /// {
    645     ///   "eval": PRFInputs
    646     /// }
    647     /// // PRFInputs:
    648     /// {
    649     ///   "first": <base64url-encoded string>,
    650     ///   "second": null | <base64url-encoded string>
    651     /// }
    652     /// ```
    653     ///
    654     /// where the only required fields are `"eval"` and `"first"`.
    655     ///
    656     /// All extensions are not required to have a response sent back; but _if_ a response is sent back, its value
    657     /// will be enforced.
    658     ///
    659     /// Unknown or duplicate fields lead to an error.
    660     ///
    661     /// # Examples
    662     ///
    663     /// ```
    664     /// # use webauthn_rp::request::{ExtensionReq, auth::ser::ExtensionOwned};
    665     /// let ext = serde_json::from_str::<ExtensionOwned>(
    666     ///     r#"{"prf":{"eval":{"first":"","second":null}}}"#,
    667     /// )?;
    668     /// assert!(ext.prf.map_or(false, |prf| prf.first.is_empty()
    669     ///     && prf.second.is_none()
    670     ///     && matches!(prf.ext_req, ExtensionReq::Allow)));
    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         /// `Visitor` for `ExtensionOwned`.
    679         struct ExtensionOwnedVisitor;
    680         impl<'d> Visitor<'d> for ExtensionOwnedVisitor {
    681             type Value = ExtensionOwned;
    682             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    683                 formatter.write_str("ExtensionOwned")
    684             }
    685             fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
    686             where
    687                 A: MapAccess<'d>,
    688             {
    689                 /// Field for `ExtensionOwned`.
    690                 struct Field;
    691                 impl<'e> Deserialize<'e> for Field {
    692                     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    693                     where
    694                         D: Deserializer<'e>,
    695                     {
    696                         /// `Visitor` for `Field`.
    697                         struct FieldVisitor;
    698                         impl Visitor<'_> for FieldVisitor {
    699                             type Value = Field;
    700                             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    701                                 write!(formatter, "'{PRF}'")
    702                             }
    703                             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    704                             where
    705                                 E: Error,
    706                             {
    707                                 if v == PRF {
    708                                     Ok(Field)
    709                                 } else {
    710                                     Err(E::unknown_field(v, FIELDS))
    711                                 }
    712                             }
    713                         }
    714                         deserializer.deserialize_identifier(FieldVisitor)
    715                     }
    716                 }
    717                 map.next_key::<Field>().and_then(|opt_key| {
    718                     opt_key
    719                         .map_or_else(
    720                             || Ok(None),
    721                             |_k| {
    722                                 map.next_value::<Option<PrfHelper>>().and_then(|prf| {
    723                                     map.next_key::<Field>().and_then(|opt_key2| {
    724                                         opt_key2.map_or_else(
    725                                             || Ok(prf.map(|val| val.0)),
    726                                             |_k2| Err(Error::duplicate_field(PRF)),
    727                                         )
    728                                     })
    729                                 })
    730                             },
    731                         )
    732                         .map(|prf| ExtensionOwned { prf })
    733                 })
    734             }
    735         }
    736         /// `"prf"`.
    737         const PRF: &str = "prf";
    738         /// Fields for `ExtensionOwned`.
    739         const FIELDS: &[&str; 1] = &[PRF];
    740         deserializer.deserialize_struct("ExtensionOwned", FIELDS, ExtensionOwnedVisitor)
    741     }
    742 }
    743 /// Error returned by [`PublicKeyCredentialRequestOptionsOwned::as_options`] when
    744 /// [`PublicKeyCredentialRequestOptionsOwned::rp_id`] is `None`.
    745 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    746 pub struct PublicKeyCredentialRequestOptionsOwnedErr;
    747 impl Display for PublicKeyCredentialRequestOptionsOwnedErr {
    748     #[inline]
    749     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    750         f.write_str("request options did not have an RP ID")
    751     }
    752 }
    753 impl E for PublicKeyCredentialRequestOptionsOwnedErr {}
    754 /// Similar to [`PublicKeyCredentialRequestOptions`] except the fields are based on owned data, and
    755 /// [`Self::rp_id`] is optional.
    756 ///
    757 /// This is primarily useful to assist [`ClientCredentialRequestOptions::deserialize`],
    758 #[derive(Debug)]
    759 pub struct PublicKeyCredentialRequestOptionsOwned {
    760     /// See [`PublicKeyCredentialRequestOptions::rp_id`].
    761     pub rp_id: Option<RpId>,
    762     /// See [`PublicKeyCredentialRequestOptions::timeout`].
    763     pub timeout: NonZeroU32,
    764     /// See [`PublicKeyCredentialRequestOptions::user_verification`].
    765     pub user_verification: UserVerificationRequirement,
    766     /// See [`PublicKeyCredentialRequestOptions::hints`].
    767     pub hints: Hints,
    768     /// See [`PublicKeyCredentialRequestOptions::extensions`].
    769     pub extensions: ExtensionOwned,
    770 }
    771 impl PublicKeyCredentialRequestOptionsOwned {
    772     /// Returns a `PublicKeyCredentialRequestOptions` based on `self`.
    773     ///
    774     /// # Errors
    775     ///
    776     /// Errors iff [`Self::rp_id`] is `None`.
    777     #[inline]
    778     pub fn as_options(
    779         &self,
    780     ) -> Result<
    781         PublicKeyCredentialRequestOptions<'_, '_, '_>,
    782         PublicKeyCredentialRequestOptionsOwnedErr,
    783     > {
    784         self.rp_id
    785             .as_ref()
    786             .ok_or(PublicKeyCredentialRequestOptionsOwnedErr)
    787             .map(|rp_id| PublicKeyCredentialRequestOptions {
    788                 challenge: Challenge::new(),
    789                 timeout: self.timeout,
    790                 rp_id,
    791                 user_verification: self.user_verification,
    792                 hints: self.hints,
    793                 extensions: self.extensions.as_extension(),
    794             })
    795     }
    796     /// Returns a `PublicKeyCredentialRequestOptions` based on `self` and `rp_id`.
    797     ///
    798     /// Note `rp_id` is used _unconditionally_ regardless if [`Self::rp_id`] is `Some`.
    799     #[inline]
    800     #[must_use]
    801     pub fn with_rp_id<'rp_id>(
    802         &self,
    803         rp_id: &'rp_id RpId,
    804     ) -> PublicKeyCredentialRequestOptions<'rp_id, '_, '_> {
    805         PublicKeyCredentialRequestOptions {
    806             challenge: Challenge::new(),
    807             timeout: self.timeout,
    808             rp_id,
    809             user_verification: self.user_verification,
    810             hints: self.hints,
    811             extensions: self.extensions.as_extension(),
    812         }
    813     }
    814     /// Returns a `PublicKeyCredentialRequestOptions` based on `self`, `exclude_credentials`, and `extensions`.
    815     ///
    816     /// Note `extensions` is used _unconditionally_ regardless of what [`Self::extensions`] is.
    817     ///
    818     /// # Errors
    819     ///
    820     /// Errors iff [`Self::rp_id`] is `None`.
    821     #[inline]
    822     pub fn with_extensions<'prf_first, 'prf_second>(
    823         &self,
    824         extensions: Extension<'prf_first, 'prf_second>,
    825     ) -> Result<
    826         PublicKeyCredentialRequestOptions<'_, 'prf_first, 'prf_second>,
    827         PublicKeyCredentialRequestOptionsOwnedErr,
    828     > {
    829         self.rp_id
    830             .as_ref()
    831             .ok_or(PublicKeyCredentialRequestOptionsOwnedErr)
    832             .map(|rp_id| PublicKeyCredentialRequestOptions {
    833                 challenge: Challenge::new(),
    834                 timeout: self.timeout,
    835                 rp_id,
    836                 user_verification: self.user_verification,
    837                 hints: self.hints,
    838                 extensions,
    839             })
    840     }
    841     /// Returns a `PublicKeyCredentialRequestOptions` based on `self`, `rp_id`, and `extensions`.
    842     ///
    843     /// Note `rp_id` and `extensions` are used _unconditionally_ regardless if [`Self::rp_id`] is `Some` or what
    844     /// [`Self::extensions`] is.
    845     #[inline]
    846     #[must_use]
    847     pub fn with_rp_id_and_extensions<'rp_id, 'prf_first, 'prf_second>(
    848         &self,
    849         rp_id: &'rp_id RpId,
    850         extensions: Extension<'prf_first, 'prf_second>,
    851     ) -> PublicKeyCredentialRequestOptions<'rp_id, 'prf_first, 'prf_second> {
    852         PublicKeyCredentialRequestOptions {
    853             challenge: Challenge::new(),
    854             timeout: self.timeout,
    855             rp_id,
    856             user_verification: self.user_verification,
    857             hints: self.hints,
    858             extensions,
    859         }
    860     }
    861 }
    862 impl Default for PublicKeyCredentialRequestOptionsOwned {
    863     #[inline]
    864     fn default() -> Self {
    865         Self {
    866             rp_id: None,
    867             timeout: FIVE_MINUTES,
    868             user_verification: UserVerificationRequirement::Preferred,
    869             hints: Hints::default(),
    870             extensions: ExtensionOwned::default(),
    871         }
    872     }
    873 }
    874 impl<'de> Deserialize<'de> for PublicKeyCredentialRequestOptionsOwned {
    875     /// Deserializes a `struct` based on
    876     /// [`PublicKeyCredentialRequestOptionsJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialrequestoptionsjson).
    877     ///
    878     /// Note that none of the fields are required, and all are allowed to be `null`.
    879     ///
    880     /// If [`challenge`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptionsjson-challenge)
    881     /// exists, it must be `null`. If
    882     /// [`allowCredentials`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptionsjson-allowcredentials)
    883     /// exists, it must be `null` or empty.
    884     ///
    885     /// If [`timeout`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptionsjson-timeout) exists,
    886     /// it must be `null` or positive. If `timeout` is missing or is `null`, then [`FIVE_MINUTES`] will be used.
    887     ///
    888     /// If `userVerification` is missing or is `null`, then [`UserVerificationRequirement::Required`] will be used.
    889     ///
    890     /// Unknown or duplicate fields lead to an error.
    891     #[expect(clippy::too_many_lines, reason = "131 lines is fine")]
    892     #[inline]
    893     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    894     where
    895         D: Deserializer<'de>,
    896     {
    897         /// `Visitor` for `PublicKeyCredentialRequestOptionsOwned`.
    898         struct PublicKeyCredentialRequestOptionsOwnedVisitor;
    899         impl<'d> Visitor<'d> for PublicKeyCredentialRequestOptionsOwnedVisitor {
    900             type Value = PublicKeyCredentialRequestOptionsOwned;
    901             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    902                 formatter.write_str("PublicKeyCredentialRequestOptionsOwned")
    903             }
    904             #[expect(clippy::too_many_lines, reason = "104 lines is fine")]
    905             fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
    906             where
    907                 A: MapAccess<'d>,
    908             {
    909                 /// Field for `PublicKeyCredentialRequestOptionsOwned`.
    910                 enum Field {
    911                     /// `rpId`.
    912                     RpId,
    913                     /// `userVerification`.
    914                     UserVerification,
    915                     /// `challenge`.
    916                     Challenge,
    917                     /// `timeout`.
    918                     Timeout,
    919                     /// `allowCredentials`.
    920                     AllowCredentials,
    921                     /// `hints`.
    922                     Hints,
    923                     /// `extensions`.
    924                     Extensions,
    925                 }
    926                 impl<'e> Deserialize<'e> for Field {
    927                     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    928                     where
    929                         D: Deserializer<'e>,
    930                     {
    931                         /// `Visitor` for `Field`.
    932                         struct FieldVisitor;
    933                         impl Visitor<'_> for FieldVisitor {
    934                             type Value = Field;
    935                             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    936                                 write!(
    937                                     formatter,
    938                                     "'{RP_ID}', '{USER_VERIFICATION}', '{CHALLENGE}', '{TIMEOUT}', '{ALLOW_CREDENTIALS}', '{HINTS}', or '{EXTENSIONS}'"
    939                                 )
    940                             }
    941                             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    942                             where
    943                                 E: Error,
    944                             {
    945                                 match v {
    946                                     RP_ID => Ok(Field::RpId),
    947                                     USER_VERIFICATION => Ok(Field::UserVerification),
    948                                     CHALLENGE => Ok(Field::Challenge),
    949                                     TIMEOUT => Ok(Field::Timeout),
    950                                     ALLOW_CREDENTIALS => Ok(Field::AllowCredentials),
    951                                     HINTS => Ok(Field::Hints),
    952                                     EXTENSIONS => Ok(Field::Extensions),
    953                                     _ => Err(E::unknown_field(v, FIELDS)),
    954                                 }
    955                             }
    956                         }
    957                         deserializer.deserialize_identifier(FieldVisitor)
    958                     }
    959                 }
    960                 let mut rp = None;
    961                 let mut user_veri = None;
    962                 let mut chall = None;
    963                 let mut time = None;
    964                 let mut allow = None;
    965                 let mut hint = None;
    966                 let mut ext = None;
    967                 while let Some(key) = map.next_key()? {
    968                     match key {
    969                         Field::RpId => {
    970                             if rp.is_some() {
    971                                 return Err(Error::duplicate_field(RP_ID));
    972                             }
    973                             rp = map.next_value::<Option<_>>().map(Some)?;
    974                         }
    975                         Field::UserVerification => {
    976                             if user_veri.is_some() {
    977                                 return Err(Error::duplicate_field(USER_VERIFICATION));
    978                             }
    979                             user_veri = map.next_value::<Option<_>>().map(Some)?;
    980                         }
    981                         Field::Challenge => {
    982                             if chall.is_some() {
    983                                 return Err(Error::duplicate_field(CHALLENGE));
    984                             }
    985                             chall = map.next_value::<Null>().map(Some)?;
    986                         }
    987                         Field::Timeout => {
    988                             if time.is_some() {
    989                                 return Err(Error::duplicate_field(TIMEOUT));
    990                             }
    991                             time = map.next_value::<Option<_>>().map(Some)?;
    992                         }
    993                         Field::AllowCredentials => {
    994                             if allow.is_some() {
    995                                 return Err(Error::duplicate_field(ALLOW_CREDENTIALS));
    996                             }
    997                             allow = map.next_value::<Option<[(); 0]>>().map(Some)?;
    998                         }
    999                         Field::Hints => {
   1000                             if hint.is_some() {
   1001                                 return Err(Error::duplicate_field(HINTS));
   1002                             }
   1003                             hint = map.next_value::<Option<_>>().map(Some)?;
   1004                         }
   1005                         Field::Extensions => {
   1006                             if ext.is_some() {
   1007                                 return Err(Error::duplicate_field(EXTENSIONS));
   1008                             }
   1009                             ext = map.next_value::<Option<_>>().map(Some)?;
   1010                         }
   1011                     }
   1012                 }
   1013                 Ok(PublicKeyCredentialRequestOptionsOwned {
   1014                     rp_id: rp.flatten(),
   1015                     user_verification: user_veri
   1016                         .flatten()
   1017                         .unwrap_or(UserVerificationRequirement::Preferred),
   1018                     timeout: time.flatten().unwrap_or(FIVE_MINUTES),
   1019                     extensions: ext.flatten().unwrap_or_default(),
   1020                     hints: hint.flatten().unwrap_or_default(),
   1021                 })
   1022             }
   1023         }
   1024         /// Fields for `PublicKeyCredentialRequestOptionsOwned`.
   1025         const FIELDS: &[&str; 7] = &[
   1026             RP_ID,
   1027             USER_VERIFICATION,
   1028             CHALLENGE,
   1029             TIMEOUT,
   1030             ALLOW_CREDENTIALS,
   1031             HINTS,
   1032             EXTENSIONS,
   1033         ];
   1034         deserializer.deserialize_struct(
   1035             "PublicKeyCredentialRequestOptionsOwned",
   1036             FIELDS,
   1037             PublicKeyCredentialRequestOptionsOwnedVisitor,
   1038         )
   1039     }
   1040 }
   1041 /// Deserializes client-supplied data to assist in the creation of [`DiscoverableCredentialRequestOptions`]
   1042 /// and [`NonDiscoverableCredentialRequestOptions`].
   1043 ///
   1044 /// It's common to tailor an authentication ceremony based on a user's environment. The options that should be
   1045 /// used are then sent to the server. To facilitate this, [`Self::deserialize`] can be used to deserialize the data
   1046 /// sent from the client.
   1047 ///
   1048 /// Note one may want to change some of the [`Extension`] data since [`ExtensionReq::Allow`] is unconditionally
   1049 /// used. Read [`ExtensionOwned::deserialize`] for more information.
   1050 ///
   1051 /// Additionally, one may want to change the value of [`PublicKeyCredentialRequestOptions::rp_id`] since
   1052 /// `"example.invalid"` is used in the event the RP ID was not supplied.
   1053 #[derive(Debug)]
   1054 pub struct ClientCredentialRequestOptions {
   1055     /// See [`DiscoverableCredentialRequestOptions::mediation`] and
   1056     /// [`NonDiscoverableCredentialRequestOptions::mediation`].
   1057     pub mediation: CredentialMediationRequirement,
   1058     /// See [`DiscoverableCredentialRequestOptions::mediation`].
   1059     pub ui_mode: Option<CredentialUiMode>,
   1060     /// See [`DiscoverableCredentialRequestOptions::public_key`] and
   1061     /// See [`NonDiscoverableCredentialRequestOptions::options`].
   1062     pub public_key: PublicKeyCredentialRequestOptionsOwned,
   1063 }
   1064 impl<'de> Deserialize<'de> for ClientCredentialRequestOptions {
   1065     /// Deserializes a `struct` according to the following pseudo-schema:
   1066     ///
   1067     /// ```json
   1068     /// {
   1069     ///   "mediation": null | "required" | "conditional",
   1070     ///   "uiMode": null | "immediate",
   1071     ///   "publicKey": null | <PublicKeyCredentialRequestOptionsOwned>
   1072     /// }
   1073     /// ```
   1074     ///
   1075     /// where none of the fields are required and `"publicKey"` is deserialized according to
   1076     /// [`PublicKeyCredentialRequestOptionsOwned::deserialize`]. If any field is missing or is `null`, then
   1077     /// the corresponding [`Default`] `impl` will be used.
   1078     ///
   1079     /// Unknown or duplicate fields lead to an error.
   1080     #[inline]
   1081     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1082     where
   1083         D: Deserializer<'de>,
   1084     {
   1085         /// `Visitor` for `ClientCredentialRequestOptions`.
   1086         struct ClientCredentialRequestOptionsVisitor;
   1087         impl<'d> Visitor<'d> for ClientCredentialRequestOptionsVisitor {
   1088             type Value = ClientCredentialRequestOptions;
   1089             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   1090                 formatter.write_str("ClientCredentialRequestOptions")
   1091             }
   1092             fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
   1093             where
   1094                 A: MapAccess<'d>,
   1095             {
   1096                 /// Field in `ClientCredentialRequestOptions`.
   1097                 enum Field {
   1098                     /// `mediation`.
   1099                     Mediation,
   1100                     /// `uiMode`.
   1101                     UiMode,
   1102                     /// `publicKey`
   1103                     PublicKey,
   1104                 }
   1105                 impl<'e> Deserialize<'e> for Field {
   1106                     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1107                     where
   1108                         D: Deserializer<'e>,
   1109                     {
   1110                         /// `Visitor` for `Field`.
   1111                         struct FieldVisitor;
   1112                         impl Visitor<'_> for FieldVisitor {
   1113                             type Value = Field;
   1114                             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   1115                                 write!(formatter, "'{MEDIATION}', '{UI_MODE}', or '{PUBLIC_KEY}'")
   1116                             }
   1117                             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
   1118                             where
   1119                                 E: Error,
   1120                             {
   1121                                 match v {
   1122                                     MEDIATION => Ok(Field::Mediation),
   1123                                     UI_MODE => Ok(Field::UiMode),
   1124                                     PUBLIC_KEY => Ok(Field::PublicKey),
   1125                                     _ => Err(E::unknown_field(v, FIELDS)),
   1126                                 }
   1127                             }
   1128                         }
   1129                         deserializer.deserialize_identifier(FieldVisitor)
   1130                     }
   1131                 }
   1132                 let mut med = None;
   1133                 let mut ui = None;
   1134                 let mut key = None;
   1135                 while let Some(k) = map.next_key()? {
   1136                     match k {
   1137                         Field::Mediation => {
   1138                             if med.is_some() {
   1139                                 return Err(Error::duplicate_field(MEDIATION));
   1140                             }
   1141                             med = map.next_value::<Option<_>>().map(Some)?;
   1142                         }
   1143                         Field::UiMode => {
   1144                             if ui.is_some() {
   1145                                 return Err(Error::duplicate_field(UI_MODE));
   1146                             }
   1147                             ui = map.next_value().map(Some)?;
   1148                         }
   1149                         Field::PublicKey => {
   1150                             if key.is_some() {
   1151                                 return Err(Error::duplicate_field(PUBLIC_KEY));
   1152                             }
   1153                             key = map.next_value::<Option<_>>().map(Some)?;
   1154                         }
   1155                     }
   1156                 }
   1157                 Ok(ClientCredentialRequestOptions {
   1158                     mediation: med.flatten().unwrap_or_default(),
   1159                     ui_mode: ui.flatten(),
   1160                     public_key: key.flatten().unwrap_or_default(),
   1161                 })
   1162             }
   1163         }
   1164         /// Fields for `ClientCredentialRequestOptions`.
   1165         const FIELDS: &[&str; 3] = &[MEDIATION, UI_MODE, PUBLIC_KEY];
   1166         deserializer.deserialize_struct(
   1167             "ClientCredentialRequestOptions",
   1168             FIELDS,
   1169             ClientCredentialRequestOptionsVisitor,
   1170         )
   1171     }
   1172 }