webauthn_rs_proto/
auth.rs1#[cfg(feature = "wasm")]
4use base64::Engine;
5use base64urlsafedata::Base64UrlSafeData;
6use serde::{Deserialize, Serialize};
7
8use crate::extensions::{AuthenticationExtensionsClientOutputs, RequestAuthenticationExtensions};
9use crate::options::*;
10#[cfg(feature = "wasm")]
11use crate::BASE64_ENGINE;
12
13#[derive(Debug, Serialize, Clone, Deserialize)]
15#[serde(rename_all = "camelCase")]
16pub struct PublicKeyCredentialRequestOptions {
17 pub challenge: Base64UrlSafeData,
19 #[serde(skip_serializing_if = "Option::is_none")]
21 pub timeout: Option<u32>,
22 pub rp_id: String,
24 pub allow_credentials: Vec<AllowCredentials>,
26 pub user_verification: UserVerificationPolicy,
28
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub hints: Option<Vec<PublicKeyCredentialHints>>,
32
33 #[serde(skip_serializing_if = "Option::is_none")]
35 pub extensions: Option<RequestAuthenticationExtensions>,
36}
37
38#[derive(Debug, Serialize, Clone, Deserialize)]
41#[serde(rename_all = "camelCase")]
42pub enum Mediation {
43 Conditional,
54 }
57
58#[derive(Debug, Serialize, Clone, Deserialize)]
63#[serde(rename_all = "camelCase")]
64pub struct RequestChallengeResponse {
65 pub public_key: PublicKeyCredentialRequestOptions,
67 #[serde(default, skip_serializing_if = "Option::is_none")]
68 pub mediation: Option<Mediation>,
70}
71
72#[cfg(feature = "wasm")]
73impl From<RequestChallengeResponse> for web_sys::CredentialRequestOptions {
74 fn from(rcr: RequestChallengeResponse) -> Self {
75 use js_sys::{Array, Object, Uint8Array};
76 use wasm_bindgen::JsValue;
77
78 let jsv = serde_wasm_bindgen::to_value(&rcr).unwrap();
79 let pkcco = js_sys::Reflect::get(&jsv, &"publicKey".into()).unwrap();
80
81 let chal = Uint8Array::from(rcr.public_key.challenge.as_slice());
82 js_sys::Reflect::set(&pkcco, &"challenge".into(), &chal).unwrap();
83
84 if let Some(extensions) = rcr.public_key.extensions {
85 let obj: Object = (&extensions).into();
86 js_sys::Reflect::set(&pkcco, &"extensions".into(), &obj).unwrap();
87 }
88
89 let allow_creds: Array = rcr
90 .public_key
91 .allow_credentials
92 .iter()
93 .map(|ac| {
94 let obj = Object::new();
95 js_sys::Reflect::set(&obj, &"type".into(), &JsValue::from_str(ac.type_.as_str()))
96 .unwrap();
97
98 js_sys::Reflect::set(&obj, &"id".into(), &Uint8Array::from(ac.id.as_slice()))
99 .unwrap();
100
101 if let Some(transports) = &ac.transports {
102 let tarray: Array = transports
103 .iter()
104 .map(|trs| serde_wasm_bindgen::to_value(trs).unwrap())
105 .collect();
106
107 js_sys::Reflect::set(&obj, &"transports".into(), &tarray).unwrap();
108 }
109
110 obj
111 })
112 .collect();
113 js_sys::Reflect::set(&pkcco, &"allowCredentials".into(), &allow_creds).unwrap();
114
115 web_sys::CredentialRequestOptions::from(jsv)
116 }
117}
118
119#[derive(Debug, Deserialize, Serialize, Clone)]
121pub struct AuthenticatorAssertionResponseRaw {
122 #[serde(rename = "authenticatorData")]
124 pub authenticator_data: Base64UrlSafeData,
125
126 #[serde(rename = "clientDataJSON")]
128 pub client_data_json: Base64UrlSafeData,
129
130 pub signature: Base64UrlSafeData,
132
133 #[serde(rename = "userHandle")]
135 pub user_handle: Option<Base64UrlSafeData>,
136}
137
138#[derive(Debug, Deserialize, Serialize, Clone)]
145pub struct PublicKeyCredential {
146 pub id: String,
148 #[serde(rename = "rawId")]
150 pub raw_id: Base64UrlSafeData,
151 pub response: AuthenticatorAssertionResponseRaw,
153 #[serde(default, alias = "clientExtensionResults")]
155 pub extensions: AuthenticationExtensionsClientOutputs,
156 #[serde(rename = "type")]
158 pub type_: String,
159}
160
161impl PublicKeyCredential {
162 pub fn get_user_unique_id(&self) -> Option<&[u8]> {
165 self.response.user_handle.as_ref().map(|b| b.as_ref())
166 }
167
168 pub fn get_credential_id(&self) -> &[u8] {
170 self.raw_id.as_slice()
171 }
172}
173
174#[cfg(feature = "wasm")]
175impl From<web_sys::PublicKeyCredential> for PublicKeyCredential {
176 fn from(data: web_sys::PublicKeyCredential) -> PublicKeyCredential {
177 use js_sys::Uint8Array;
178
179 let data_raw_id =
180 Uint8Array::new(&js_sys::Reflect::get(&data, &"rawId".into()).unwrap()).to_vec();
181
182 let data_response = js_sys::Reflect::get(&data, &"response".into()).unwrap();
183
184 let data_response_authenticator_data = Uint8Array::new(
185 &js_sys::Reflect::get(&data_response, &"authenticatorData".into()).unwrap(),
186 )
187 .to_vec();
188
189 let data_response_signature =
190 Uint8Array::new(&js_sys::Reflect::get(&data_response, &"signature".into()).unwrap())
191 .to_vec();
192
193 let data_response_user_handle =
194 &js_sys::Reflect::get(&data_response, &"userHandle".into()).unwrap();
195 let data_response_user_handle = if data_response_user_handle.is_undefined() {
196 None
197 } else {
198 Some(Uint8Array::new(data_response_user_handle).to_vec())
199 };
200
201 let data_response_client_data_json = Uint8Array::new(
202 &js_sys::Reflect::get(&data_response, &"clientDataJSON".into()).unwrap(),
203 )
204 .to_vec();
205
206 let data_extensions = data.get_client_extension_results();
207 web_sys::console::log_1(&data_extensions);
208
209 PublicKeyCredential {
210 id: BASE64_ENGINE.encode(&data_raw_id),
211 raw_id: data_raw_id.into(),
212 response: AuthenticatorAssertionResponseRaw {
213 authenticator_data: data_response_authenticator_data.into(),
214 client_data_json: data_response_client_data_json.into(),
215 signature: data_response_signature.into(),
216 user_handle: data_response_user_handle.map(Into::into),
217 },
218 extensions: data_extensions.into(),
219 type_: "public-key".to_string(),
220 }
221 }
222}