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