1#[cfg(any(feature = "tokio", test))]
2use std::sync::Arc;
3
4use passkey_types::{
5 Passkey,
6 ctap2::{
7 Ctap2Error, StatusCode,
8 make_credential::{Options, PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity},
9 },
10 webauthn::PublicKeyCredentialDescriptor,
11};
12
13use crate::passkey::PasskeyAccessor;
14
15pub struct StoreInfo {
17 pub discoverability: DiscoverabilitySupport,
19}
20
21#[derive(PartialEq)]
24pub enum DiscoverabilitySupport {
25 Full,
27
28 OnlyNonDiscoverable,
31
32 ForcedDiscoverable,
35}
36
37impl DiscoverabilitySupport {
38 pub fn is_passkey_discoverable(&self, rk_input: bool) -> bool {
40 match self {
41 DiscoverabilitySupport::Full => rk_input,
42 DiscoverabilitySupport::OnlyNonDiscoverable => false,
43 DiscoverabilitySupport::ForcedDiscoverable => true,
44 }
45 }
46}
47
48#[async_trait::async_trait]
50pub trait CredentialStore {
51 type PasskeyItem: PasskeyAccessor + Send + Sync;
53
54 async fn find_credentials(
59 &self,
60 ids: Option<&[PublicKeyCredentialDescriptor]>,
61 rp_id: &str,
62 user_handle: Option<&[u8]>,
63 ) -> Result<Vec<Self::PasskeyItem>, StatusCode>;
64
65 async fn save_credential(
67 &mut self,
68 cred: Passkey,
69 user: PublicKeyCredentialUserEntity,
70 rp: PublicKeyCredentialRpEntity,
71 options: Options,
72 ) -> Result<(), StatusCode>;
73
74 async fn update_credential(&mut self, cred: &Self::PasskeyItem) -> Result<(), StatusCode>;
76
77 async fn get_info(&self) -> StoreInfo;
79}
80
81pub type MemoryStore = std::collections::HashMap<Vec<u8>, Passkey>;
85
86#[async_trait::async_trait]
87impl CredentialStore for MemoryStore {
88 type PasskeyItem = Passkey;
89
90 async fn find_credentials(
91 &self,
92 allow_credentials: Option<&[PublicKeyCredentialDescriptor]>,
93 _rp_id: &str,
94 _user_handle: Option<&[u8]>,
95 ) -> Result<Vec<Self::PasskeyItem>, StatusCode> {
96 let creds: Vec<Passkey> = allow_credentials
97 .into_iter()
98 .flatten()
99 .filter_map(|id| self.get(&*id.id))
100 .cloned()
101 .collect();
102 if creds.is_empty() {
103 Err(Ctap2Error::NoCredentials.into())
104 } else {
105 Ok(creds)
106 }
107 }
108
109 async fn save_credential(
110 &mut self,
111 cred: Passkey,
112 _user: PublicKeyCredentialUserEntity,
113 _rp: PublicKeyCredentialRpEntity,
114 _options: Options,
115 ) -> Result<(), StatusCode> {
116 self.insert(cred.credential_id.clone().into(), cred);
117 Ok(())
118 }
119
120 async fn update_credential(&mut self, cred: &Passkey) -> Result<(), StatusCode> {
121 self.insert(cred.credential_id.clone().into(), cred.clone());
122 Ok(())
123 }
124
125 async fn get_info(&self) -> StoreInfo {
126 StoreInfo {
127 discoverability: DiscoverabilitySupport::ForcedDiscoverable,
128 }
129 }
130}
131
132#[async_trait::async_trait]
133impl CredentialStore for Option<Passkey> {
134 type PasskeyItem = Passkey;
135
136 async fn find_credentials(
137 &self,
138 id: Option<&[PublicKeyCredentialDescriptor]>,
139 _rp_id: &str,
140 _user_handle: Option<&[u8]>,
141 ) -> Result<Vec<Self::PasskeyItem>, StatusCode> {
142 if let Some(id) = id {
143 id.iter().find_map(|id| {
144 self.clone().filter(|pk| pk.credential_id == id.id)
146 })
147 } else {
148 self.clone() }
150 .map(|pk| vec![pk])
151 .ok_or(Ctap2Error::NoCredentials.into())
152 }
153
154 async fn save_credential(
155 &mut self,
156 cred: Passkey,
157 _user: PublicKeyCredentialUserEntity,
158 _rp: PublicKeyCredentialRpEntity,
159 _options: Options,
160 ) -> Result<(), StatusCode> {
161 self.replace(cred);
162 Ok(())
163 }
164
165 async fn update_credential(&mut self, cred: &Passkey) -> Result<(), StatusCode> {
166 self.replace(cred.clone());
167 Ok(())
168 }
169
170 async fn get_info(&self) -> StoreInfo {
171 StoreInfo {
172 discoverability: DiscoverabilitySupport::ForcedDiscoverable,
173 }
174 }
175}
176
177#[cfg(any(feature = "tokio", test))]
178#[async_trait::async_trait]
179impl<S: CredentialStore<PasskeyItem = Passkey> + Send + Sync> CredentialStore
180 for Arc<tokio::sync::Mutex<S>>
181{
182 type PasskeyItem = Passkey;
183
184 async fn find_credentials(
185 &self,
186 ids: Option<&[PublicKeyCredentialDescriptor]>,
187 rp_id: &str,
188 user_handle: Option<&[u8]>,
189 ) -> Result<Vec<Self::PasskeyItem>, StatusCode> {
190 self.lock()
191 .await
192 .find_credentials(ids, rp_id, user_handle)
193 .await
194 }
195
196 async fn save_credential(
197 &mut self,
198 cred: Passkey,
199 user: PublicKeyCredentialUserEntity,
200 rp: PublicKeyCredentialRpEntity,
201 options: Options,
202 ) -> Result<(), StatusCode> {
203 self.lock()
204 .await
205 .save_credential(cred, user, rp, options)
206 .await
207 }
208
209 async fn update_credential(&mut self, cred: &Passkey) -> Result<(), StatusCode> {
210 self.lock().await.update_credential(cred).await
211 }
212
213 async fn get_info(&self) -> StoreInfo {
214 self.lock().await.get_info().await
215 }
216}
217
218#[cfg(any(feature = "tokio", test))]
219#[async_trait::async_trait]
220impl<S: CredentialStore<PasskeyItem = Passkey> + Send + Sync> CredentialStore
221 for Arc<tokio::sync::RwLock<S>>
222{
223 type PasskeyItem = Passkey;
224
225 async fn find_credentials(
226 &self,
227 ids: Option<&[PublicKeyCredentialDescriptor]>,
228 rp_id: &str,
229 user_handle: Option<&[u8]>,
230 ) -> Result<Vec<Self::PasskeyItem>, StatusCode> {
231 self.read()
232 .await
233 .find_credentials(ids, rp_id, user_handle)
234 .await
235 }
236
237 async fn save_credential(
238 &mut self,
239 cred: Passkey,
240 user: PublicKeyCredentialUserEntity,
241 rp: PublicKeyCredentialRpEntity,
242 options: Options,
243 ) -> Result<(), StatusCode> {
244 self.write()
245 .await
246 .save_credential(cred, user, rp, options)
247 .await
248 }
249
250 async fn update_credential(&mut self, cred: &Passkey) -> Result<(), StatusCode> {
251 self.write().await.update_credential(cred).await
252 }
253
254 async fn get_info(&self) -> StoreInfo {
255 self.read().await.get_info().await
256 }
257}
258
259#[cfg(any(feature = "tokio", test))]
260#[async_trait::async_trait]
261impl<S: CredentialStore<PasskeyItem = Passkey> + Send + Sync> CredentialStore
262 for tokio::sync::Mutex<S>
263{
264 type PasskeyItem = Passkey;
265
266 async fn find_credentials(
267 &self,
268 ids: Option<&[PublicKeyCredentialDescriptor]>,
269 rp_id: &str,
270 user_handle: Option<&[u8]>,
271 ) -> Result<Vec<Self::PasskeyItem>, StatusCode> {
272 self.lock()
273 .await
274 .find_credentials(ids, rp_id, user_handle)
275 .await
276 }
277
278 async fn save_credential(
279 &mut self,
280 cred: Passkey,
281 user: PublicKeyCredentialUserEntity,
282 rp: PublicKeyCredentialRpEntity,
283 options: Options,
284 ) -> Result<(), StatusCode> {
285 self.lock()
286 .await
287 .save_credential(cred, user, rp, options)
288 .await
289 }
290
291 async fn update_credential(&mut self, cred: &Passkey) -> Result<(), StatusCode> {
292 self.lock().await.update_credential(cred).await
293 }
294
295 async fn get_info(&self) -> StoreInfo {
296 self.lock().await.get_info().await
297 }
298}
299
300#[cfg(any(feature = "tokio", test))]
301#[async_trait::async_trait]
302impl<S: CredentialStore<PasskeyItem = Passkey> + Send + Sync> CredentialStore
303 for tokio::sync::RwLock<S>
304{
305 type PasskeyItem = Passkey;
306
307 async fn find_credentials(
308 &self,
309 ids: Option<&[PublicKeyCredentialDescriptor]>,
310 rp_id: &str,
311 user_handle: Option<&[u8]>,
312 ) -> Result<Vec<Self::PasskeyItem>, StatusCode> {
313 self.read()
314 .await
315 .find_credentials(ids, rp_id, user_handle)
316 .await
317 }
318
319 async fn save_credential(
320 &mut self,
321 cred: Passkey,
322 user: PublicKeyCredentialUserEntity,
323 rp: PublicKeyCredentialRpEntity,
324 options: Options,
325 ) -> Result<(), StatusCode> {
326 self.write()
327 .await
328 .save_credential(cred, user, rp, options)
329 .await
330 }
331
332 async fn update_credential(&mut self, cred: &Passkey) -> Result<(), StatusCode> {
333 self.write().await.update_credential(cred).await
334 }
335
336 async fn get_info(&self) -> StoreInfo {
337 self.read().await.get_info().await
338 }
339}