ser.rs (15503B)
1 use super::{ 2 Challenge, CredentialId, CredentialMediationRequirement, Hint, PrfInput, 3 PublicKeyCredentialDescriptor, RpId, UserVerificationRequirement, 4 }; 5 use core::str; 6 use data_encoding::BASE64URL_NOPAD; 7 use serde::ser::{Serialize, SerializeSeq as _, SerializeStruct as _, Serializer}; 8 impl Serialize for CredentialMediationRequirement { 9 /// Serializes `self` to conform with 10 /// [`CredentialMediationRequirement`](https://www.w3.org/TR/credential-management-1/#enumdef-credentialmediationrequirement). 11 /// 12 /// # Examples 13 /// 14 /// ``` 15 /// # use webauthn_rp::request::CredentialMediationRequirement; 16 /// assert_eq!( 17 /// serde_json::to_string(&CredentialMediationRequirement::Silent)?, 18 /// r#""silent""# 19 /// ); 20 /// assert_eq!( 21 /// serde_json::to_string(&CredentialMediationRequirement::Optional)?, 22 /// r#""optional""# 23 /// ); 24 /// assert_eq!( 25 /// serde_json::to_string(&CredentialMediationRequirement::Conditional)?, 26 /// r#""conditional""# 27 /// ); 28 /// assert_eq!( 29 /// serde_json::to_string(&CredentialMediationRequirement::Required)?, 30 /// r#""required""# 31 /// ); 32 /// # Ok::<_, serde_json::Error>(()) 33 /// ``` 34 #[inline] 35 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 36 where 37 S: Serializer, 38 { 39 serializer.serialize_str(match *self { 40 Self::Silent => "silent", 41 Self::Optional => "optional", 42 Self::Conditional => "conditional", 43 Self::Required => "required", 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_mut_str( 67 self.as_array().as_slice(), 68 [0; Self::BASE64_LEN].as_mut_slice(), 69 )) 70 } 71 } 72 impl Serialize for RpId { 73 /// Serializes `self` as a [`prim@str`]. 74 /// 75 /// # Examples 76 /// 77 /// ``` 78 /// # use webauthn_rp::request::{AsciiDomain, RpId}; 79 /// assert_eq!( 80 /// serde_json::to_string(&RpId::Domain(AsciiDomain::try_from("www.example.com".to_owned()).unwrap())).unwrap(), 81 /// r#""www.example.com""# 82 /// ); 83 /// assert_eq!( 84 /// serde_json::to_string(&RpId::Url("ssh:foo".parse().unwrap())).unwrap(), 85 /// r#""ssh:foo""# 86 /// ); 87 /// # Ok::<_, serde_json::Error>(()) 88 /// ``` 89 #[inline] 90 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 91 where 92 S: Serializer, 93 { 94 serializer.serialize_str(self.as_ref()) 95 } 96 } 97 impl<T> Serialize for PublicKeyCredentialDescriptor<T> 98 where 99 CredentialId<T>: Serialize, 100 { 101 /// Serializes `self` to conform with 102 /// [`PublicKeyCredentialDescriptorJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialdescriptorjson). 103 /// 104 /// # Examples 105 /// 106 /// ``` 107 /// # #[cfg(all(feature = "bin", feature = "custom"))] 108 /// # use webauthn_rp::{bin::Decode, response::bin::DecodeAuthTransportsErr}; 109 /// # use webauthn_rp::{ 110 /// # request::PublicKeyCredentialDescriptor, 111 /// # response::{AuthTransports, CredentialId}, 112 /// # }; 113 /// /// Retrieves the `AuthTransports` associated with the unique `cred_id` 114 /// /// from the database. 115 /// # #[cfg(all(feature = "bin", feature = "custom"))] 116 /// fn get_transports(cred_id: CredentialId<&[u8]>) -> Result<AuthTransports, DecodeAuthTransportsErr> { 117 /// // ⋮ 118 /// # AuthTransports::decode(32) 119 /// } 120 /// // `CredentialId::try_from` only exists when `custom` is enabled; and even then, it is 121 /// // likely never needed since the `CredentialId` was originally sent from the client and is likely 122 /// // stored in a database which would be fetched by `UserHandle` or `Authentication::raw_id`. 123 /// # #[cfg(all(feature = "bin", feature = "custom"))] 124 /// let id = CredentialId::try_from(vec![0; 16])?; 125 /// # #[cfg(all(feature = "bin", feature = "custom"))] 126 /// let transports = get_transports((&id).into())?; 127 /// # #[cfg(all(feature = "bin", feature = "custom"))] 128 /// assert_eq!( 129 /// serde_json::to_string(&PublicKeyCredentialDescriptor { id, transports }).unwrap(), 130 /// r#"{"type":"public-key","id":"AAAAAAAAAAAAAAAAAAAAAA","transports":["usb"]}"# 131 /// ); 132 /// # Ok::<_, webauthn_rp::AggErr>(()) 133 /// ``` 134 #[inline] 135 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 136 where 137 S: Serializer, 138 { 139 serializer 140 .serialize_struct("PublicKeyCredentialDescriptor", 3) 141 .and_then(|mut ser| { 142 ser.serialize_field("type", "public-key").and_then(|()| { 143 ser.serialize_field("id", &self.id).and_then(|()| { 144 ser.serialize_field("transports", &self.transports) 145 .and_then(|()| ser.end()) 146 }) 147 }) 148 }) 149 } 150 } 151 impl Serialize for UserVerificationRequirement { 152 /// Serializes `self` to conform with 153 /// [`UserVerificationRequirement`](https://www.w3.org/TR/webauthn-3/#enumdef-userverificationrequirement). 154 /// 155 /// # Examples 156 /// 157 /// ``` 158 /// # use webauthn_rp::request::UserVerificationRequirement; 159 /// assert_eq!( 160 /// serde_json::to_string(&UserVerificationRequirement::Required)?, 161 /// r#""required""# 162 /// ); 163 /// assert_eq!( 164 /// serde_json::to_string(&UserVerificationRequirement::Discouraged)?, 165 /// r#""discouraged""# 166 /// ); 167 /// assert_eq!( 168 /// serde_json::to_string(&UserVerificationRequirement::Preferred)?, 169 /// r#""preferred""# 170 /// ); 171 /// # Ok::<_, serde_json::Error>(()) 172 /// ``` 173 #[inline] 174 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 175 where 176 S: Serializer, 177 { 178 serializer.serialize_str(match *self { 179 Self::Required => "required", 180 Self::Discouraged => "discouraged", 181 Self::Preferred => "preferred", 182 }) 183 } 184 } 185 impl Serialize for Hint { 186 /// Serializes `self` to conform with 187 /// [`hints`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptionsjson-hints). 188 /// 189 /// # Examples 190 /// 191 /// ``` 192 /// # use webauthn_rp::request::Hint; 193 /// assert_eq!( 194 /// serde_json::to_string(&Hint::None)?, 195 /// r#"[]"# 196 /// ); 197 /// assert_eq!( 198 /// serde_json::to_string(&Hint::SecurityKey)?, 199 /// r#"["security-key"]"# 200 /// ); 201 /// assert_eq!( 202 /// serde_json::to_string(&Hint::ClientDevice)?, 203 /// r#"["client-device"]"# 204 /// ); 205 /// assert_eq!( 206 /// serde_json::to_string(&Hint::Hybrid)?, 207 /// r#"["hybrid"]"# 208 /// ); 209 /// assert_eq!( 210 /// serde_json::to_string(&Hint::SecurityKeyClientDevice)?, 211 /// r#"["security-key","client-device"]"# 212 /// ); 213 /// assert_eq!( 214 /// serde_json::to_string(&Hint::ClientDeviceSecurityKey)?, 215 /// r#"["client-device","security-key"]"# 216 /// ); 217 /// assert_eq!( 218 /// serde_json::to_string(&Hint::SecurityKeyHybrid)?, 219 /// r#"["security-key","hybrid"]"# 220 /// ); 221 /// assert_eq!( 222 /// serde_json::to_string(&Hint::HybridSecurityKey)?, 223 /// r#"["hybrid","security-key"]"# 224 /// ); 225 /// assert_eq!( 226 /// serde_json::to_string(&Hint::ClientDeviceHybrid)?, 227 /// r#"["client-device","hybrid"]"# 228 /// ); 229 /// assert_eq!( 230 /// serde_json::to_string(&Hint::HybridClientDevice)?, 231 /// r#"["hybrid","client-device"]"# 232 /// ); 233 /// assert_eq!( 234 /// serde_json::to_string(&Hint::SecurityKeyClientDeviceHybrid)?, 235 /// r#"["security-key","client-device","hybrid"]"# 236 /// ); 237 /// assert_eq!( 238 /// serde_json::to_string(&Hint::SecurityKeyHybridClientDevice)?, 239 /// r#"["security-key","hybrid","client-device"]"# 240 /// ); 241 /// assert_eq!( 242 /// serde_json::to_string(&Hint::ClientDeviceSecurityKeyHybrid)?, 243 /// r#"["client-device","security-key","hybrid"]"# 244 /// ); 245 /// assert_eq!( 246 /// serde_json::to_string(&Hint::ClientDeviceHybridSecurityKey)?, 247 /// r#"["client-device","hybrid","security-key"]"# 248 /// ); 249 /// assert_eq!( 250 /// serde_json::to_string(&Hint::HybridSecurityKeyClientDevice)?, 251 /// r#"["hybrid","security-key","client-device"]"# 252 /// ); 253 /// assert_eq!( 254 /// serde_json::to_string(&Hint::HybridClientDeviceSecurityKey)?, 255 /// r#"["hybrid","client-device","security-key"]"# 256 /// ); 257 /// # Ok::<_, serde_json::Error>(()) 258 /// ``` 259 #[inline] 260 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 261 where 262 S: Serializer, 263 { 264 /// [`security-key`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialhints-security-key). 265 const SECURITY_KEY: &str = "security-key"; 266 /// [`client-device`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialhints-client-device). 267 const CLIENT_DEVICE: &str = "client-device"; 268 /// [`hybrid`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialhints-hybrid). 269 const HYBRID: &str = "hybrid"; 270 let count = match *self { 271 Self::None => 0, 272 Self::SecurityKey | Self::ClientDevice | Self::Hybrid => 1, 273 Self::SecurityKeyClientDevice 274 | Self::ClientDeviceSecurityKey 275 | Self::SecurityKeyHybrid 276 | Self::HybridSecurityKey 277 | Self::ClientDeviceHybrid 278 | Self::HybridClientDevice => 2, 279 Self::SecurityKeyClientDeviceHybrid 280 | Self::SecurityKeyHybridClientDevice 281 | Self::ClientDeviceSecurityKeyHybrid 282 | Self::ClientDeviceHybridSecurityKey 283 | Self::HybridSecurityKeyClientDevice 284 | Self::HybridClientDeviceSecurityKey => 3, 285 }; 286 serializer.serialize_seq(Some(count)).and_then(|mut ser| { 287 match *self { 288 Self::None => Ok(()), 289 Self::SecurityKey => ser.serialize_element(SECURITY_KEY), 290 Self::ClientDevice => ser.serialize_element(CLIENT_DEVICE), 291 Self::Hybrid => ser.serialize_element(HYBRID), 292 Self::SecurityKeyClientDevice => ser 293 .serialize_element(SECURITY_KEY) 294 .and_then(|()| ser.serialize_element(CLIENT_DEVICE)), 295 Self::ClientDeviceSecurityKey => ser 296 .serialize_element(CLIENT_DEVICE) 297 .and_then(|()| ser.serialize_element(SECURITY_KEY)), 298 Self::SecurityKeyHybrid => ser 299 .serialize_element(SECURITY_KEY) 300 .and_then(|()| ser.serialize_element(HYBRID)), 301 Self::HybridSecurityKey => ser 302 .serialize_element(HYBRID) 303 .and_then(|()| ser.serialize_element(SECURITY_KEY)), 304 Self::ClientDeviceHybrid => ser 305 .serialize_element(CLIENT_DEVICE) 306 .and_then(|()| ser.serialize_element(HYBRID)), 307 Self::HybridClientDevice => ser 308 .serialize_element(HYBRID) 309 .and_then(|()| ser.serialize_element(CLIENT_DEVICE)), 310 Self::SecurityKeyClientDeviceHybrid => { 311 ser.serialize_element(SECURITY_KEY).and_then(|()| { 312 ser.serialize_element(CLIENT_DEVICE) 313 .and_then(|()| ser.serialize_element(HYBRID)) 314 }) 315 } 316 Self::SecurityKeyHybridClientDevice => { 317 ser.serialize_element(SECURITY_KEY).and_then(|()| { 318 ser.serialize_element(HYBRID) 319 .and_then(|()| ser.serialize_element(CLIENT_DEVICE)) 320 }) 321 } 322 Self::ClientDeviceSecurityKeyHybrid => { 323 ser.serialize_element(CLIENT_DEVICE).and_then(|()| { 324 ser.serialize_element(SECURITY_KEY) 325 .and_then(|()| ser.serialize_element(HYBRID)) 326 }) 327 } 328 Self::ClientDeviceHybridSecurityKey => { 329 ser.serialize_element(CLIENT_DEVICE).and_then(|()| { 330 ser.serialize_element(HYBRID) 331 .and_then(|()| ser.serialize_element(SECURITY_KEY)) 332 }) 333 } 334 Self::HybridSecurityKeyClientDevice => { 335 ser.serialize_element(HYBRID).and_then(|()| { 336 ser.serialize_element(SECURITY_KEY) 337 .and_then(|()| ser.serialize_element(CLIENT_DEVICE)) 338 }) 339 } 340 Self::HybridClientDeviceSecurityKey => { 341 ser.serialize_element(HYBRID).and_then(|()| { 342 ser.serialize_element(CLIENT_DEVICE) 343 .and_then(|()| ser.serialize_element(SECURITY_KEY)) 344 }) 345 } 346 } 347 .and_then(|()| ser.end()) 348 }) 349 } 350 } 351 impl Serialize for PrfInput<'_, '_> { 352 /// Serializes `self` to conform with 353 /// [`AuthenticationExtensionsPRFValues`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfvalues). 354 /// 355 /// # Examples 356 /// 357 /// ``` 358 /// # use webauthn_rp::request::{PrfInput, ExtensionReq}; 359 /// assert_eq!( 360 /// serde_json::to_string(&PrfInput { 361 /// first: [0; 4].as_slice(), 362 /// second: Some([2; 1].as_slice()), 363 /// })?, 364 /// r#"{"first":"AAAAAA","second":"Ag"}"# 365 /// ); 366 /// # Ok::<_, serde_json::Error>(()) 367 /// ``` 368 #[expect( 369 clippy::arithmetic_side_effects, 370 reason = "comment justifies how overflow is not possible" 371 )] 372 #[inline] 373 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 374 where 375 S: Serializer, 376 { 377 serializer 378 // The max value is 1 + 1 = 2, so overflow is not an issue. 379 .serialize_struct("PrfInput", 1 + usize::from(self.second.is_some())) 380 .and_then(|mut ser| { 381 ser.serialize_field("first", BASE64URL_NOPAD.encode(self.first).as_str()) 382 .and_then(|()| { 383 self.second 384 .as_ref() 385 .map_or(Ok(()), |second| { 386 ser.serialize_field( 387 "second", 388 BASE64URL_NOPAD.encode(second).as_str(), 389 ) 390 }) 391 .and_then(|()| ser.end()) 392 }) 393 }) 394 } 395 }