1use crate::{
2 Error, User, UserId,
3 repositories::{PasskeyCredential, PasskeyRepository, UserRepository},
4 services::UserService,
5};
6use std::sync::Arc;
7
8pub struct PasskeyService<U: UserRepository, P: PasskeyRepository> {
10 user_service: Arc<UserService<U>>,
11 passkey_repository: Arc<P>,
12}
13
14impl<U: UserRepository, P: PasskeyRepository> PasskeyService<U, P> {
15 pub fn new(user_repository: Arc<U>, passkey_repository: Arc<P>) -> Self {
17 let user_service = Arc::new(UserService::new(user_repository));
18 Self {
19 user_service,
20 passkey_repository,
21 }
22 }
23
24 pub async fn register_credential(
26 &self,
27 user_id: &UserId,
28 credential_id: Vec<u8>,
29 public_key: Vec<u8>,
30 name: Option<String>,
31 ) -> Result<PasskeyCredential, Error> {
32 self.passkey_repository
33 .add_credential(user_id, credential_id, public_key, name)
34 .await
35 }
36
37 pub async fn get_user_credentials(
39 &self,
40 user_id: &UserId,
41 ) -> Result<Vec<PasskeyCredential>, Error> {
42 self.passkey_repository
43 .get_credentials_for_user(user_id)
44 .await
45 }
46
47 pub async fn get_credential(
49 &self,
50 credential_id: &[u8],
51 ) -> Result<Option<PasskeyCredential>, Error> {
52 self.passkey_repository.get_credential(credential_id).await
53 }
54
55 pub async fn authenticate_credential(
57 &self,
58 credential_id: &[u8],
59 ) -> Result<Option<User>, Error> {
60 let credential = self
62 .passkey_repository
63 .get_credential(credential_id)
64 .await?;
65
66 if let Some(cred) = credential {
67 self.passkey_repository
69 .update_last_used(credential_id)
70 .await?;
71
72 let user = self.user_service.get_user(&cred.user_id).await?;
74
75 Ok(user)
76 } else {
77 Ok(None)
78 }
79 }
80
81 pub async fn delete_credential(&self, credential_id: &[u8]) -> Result<(), Error> {
83 self.passkey_repository
84 .delete_credential(credential_id)
85 .await
86 }
87
88 pub async fn delete_user_credentials(&self, user_id: &UserId) -> Result<(), Error> {
90 self.passkey_repository.delete_all_for_user(user_id).await
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97 use crate::repositories::{PasskeyCredential, PasskeyRepository, UserRepository};
98 use crate::{User, UserId};
99 use async_trait::async_trait;
100 use chrono::{DateTime, Utc};
101 use std::collections::HashMap;
102 use std::sync::Arc;
103 use tokio::sync::Mutex;
104
105 #[derive(Debug, Clone)]
107 struct MockUser {
108 id: UserId,
109 email: String,
110 name: Option<String>,
111 created_at: DateTime<Utc>,
112 updated_at: DateTime<Utc>,
113 }
114
115 impl From<MockUser> for User {
116 fn from(user: MockUser) -> Self {
117 User {
118 id: user.id,
119 email: user.email,
120 name: user.name,
121 email_verified_at: None,
122 created_at: user.created_at,
123 updated_at: user.updated_at,
124 }
125 }
126 }
127
128 #[derive(Default)]
129 struct MockUserRepository {
130 users: Arc<Mutex<HashMap<UserId, MockUser>>>,
131 }
132
133 #[async_trait]
134 impl UserRepository for MockUserRepository {
135 async fn create(&self, new_user: crate::storage::NewUser) -> Result<User, Error> {
136 let user = MockUser {
137 id: UserId::new_random(),
138 email: new_user.email,
139 name: new_user.name,
140 created_at: Utc::now(),
141 updated_at: Utc::now(),
142 };
143
144 self.users
145 .lock()
146 .await
147 .insert(user.id.clone(), user.clone());
148 Ok(user.into())
149 }
150
151 async fn find_by_id(&self, id: &UserId) -> Result<Option<User>, Error> {
152 Ok(self.users.lock().await.get(id).cloned().map(Into::into))
153 }
154
155 async fn find_by_email(&self, email: &str) -> Result<Option<User>, Error> {
156 Ok(self
157 .users
158 .lock()
159 .await
160 .values()
161 .find(|u| u.email == email)
162 .cloned()
163 .map(Into::into))
164 }
165
166 async fn find_or_create_by_email(&self, email: &str) -> Result<User, Error> {
167 if let Some(user) = self.find_by_email(email).await? {
168 Ok(user)
169 } else {
170 let new_user = crate::storage::NewUser::builder()
171 .email(email.to_string())
172 .build()
173 .unwrap();
174 self.create(new_user).await
175 }
176 }
177
178 async fn update(&self, _user: &User) -> Result<User, Error> {
179 unimplemented!()
180 }
181
182 async fn delete(&self, _id: &UserId) -> Result<(), Error> {
183 unimplemented!()
184 }
185
186 async fn mark_email_verified(&self, _user_id: &UserId) -> Result<(), Error> {
187 Ok(())
188 }
189 }
190
191 #[derive(Default)]
192 struct MockPasskeyRepository {
193 credentials: Arc<Mutex<HashMap<Vec<u8>, PasskeyCredential>>>,
194 user_credentials: Arc<Mutex<HashMap<UserId, Vec<Vec<u8>>>>>,
195 }
196
197 #[async_trait]
198 impl PasskeyRepository for MockPasskeyRepository {
199 async fn add_credential(
200 &self,
201 user_id: &UserId,
202 credential_id: Vec<u8>,
203 public_key: Vec<u8>,
204 name: Option<String>,
205 ) -> Result<PasskeyCredential, Error> {
206 let credential = PasskeyCredential {
207 credential_id: credential_id.clone(),
208 user_id: user_id.clone(),
209 public_key,
210 name,
211 created_at: Utc::now(),
212 last_used_at: None,
213 };
214
215 self.credentials
216 .lock()
217 .await
218 .insert(credential_id.clone(), credential.clone());
219 self.user_credentials
220 .lock()
221 .await
222 .entry(user_id.clone())
223 .or_insert_with(Vec::new)
224 .push(credential_id);
225
226 Ok(credential)
227 }
228
229 async fn get_credential(
230 &self,
231 credential_id: &[u8],
232 ) -> Result<Option<PasskeyCredential>, Error> {
233 Ok(self.credentials.lock().await.get(credential_id).cloned())
234 }
235
236 async fn get_credentials_for_user(
237 &self,
238 user_id: &UserId,
239 ) -> Result<Vec<PasskeyCredential>, Error> {
240 let credentials = self.credentials.lock().await;
241 let user_creds = self.user_credentials.lock().await;
242
243 if let Some(cred_ids) = user_creds.get(user_id) {
244 let mut result = Vec::new();
245 for cred_id in cred_ids {
246 if let Some(cred) = credentials.get(cred_id) {
247 result.push(cred.clone());
248 }
249 }
250 Ok(result)
251 } else {
252 Ok(Vec::new())
253 }
254 }
255
256 async fn update_last_used(&self, credential_id: &[u8]) -> Result<(), Error> {
257 if let Some(cred) = self.credentials.lock().await.get_mut(credential_id) {
258 cred.last_used_at = Some(Utc::now());
259 }
260 Ok(())
261 }
262
263 async fn delete_credential(&self, credential_id: &[u8]) -> Result<(), Error> {
264 self.credentials.lock().await.remove(credential_id);
265 let mut user_creds = self.user_credentials.lock().await;
267 for (_, cred_ids) in user_creds.iter_mut() {
268 cred_ids.retain(|id| id != credential_id);
269 }
270 Ok(())
271 }
272
273 async fn delete_all_for_user(&self, user_id: &UserId) -> Result<(), Error> {
274 let mut user_creds = self.user_credentials.lock().await;
275 if let Some(cred_ids) = user_creds.remove(user_id) {
276 let mut credentials = self.credentials.lock().await;
277 for cred_id in cred_ids {
278 credentials.remove(&cred_id);
279 }
280 }
281 Ok(())
282 }
283 }
284
285 #[tokio::test]
286 async fn test_register_credential() {
287 let user_repo = Arc::new(MockUserRepository::default());
288 let passkey_repo = Arc::new(MockPasskeyRepository::default());
289 let service = PasskeyService::new(user_repo, passkey_repo);
290
291 let user_id = UserId::new_random();
292 let credential_id = vec![1, 2, 3, 4];
293 let public_key = vec![5, 6, 7, 8];
294 let name = Some("My Passkey".to_string());
295
296 let result = service
297 .register_credential(
298 &user_id,
299 credential_id.clone(),
300 public_key.clone(),
301 name.clone(),
302 )
303 .await;
304 assert!(result.is_ok());
305
306 let credential = result.unwrap();
307 assert_eq!(credential.credential_id, credential_id);
308 assert_eq!(credential.user_id, user_id);
309 assert_eq!(credential.public_key, public_key);
310 assert_eq!(credential.name, name);
311 }
312
313 #[tokio::test]
314 async fn test_get_user_credentials() {
315 let user_repo = Arc::new(MockUserRepository::default());
316 let passkey_repo = Arc::new(MockPasskeyRepository::default());
317 let service = PasskeyService::new(user_repo, passkey_repo);
318
319 let user_id = UserId::new_random();
320 let credential_id = vec![1, 2, 3, 4];
321 let public_key = vec![5, 6, 7, 8];
322
323 service
325 .register_credential(&user_id, credential_id.clone(), public_key, None)
326 .await
327 .unwrap();
328
329 let result = service.get_user_credentials(&user_id).await;
331 assert!(result.is_ok());
332
333 let credentials = result.unwrap();
334 assert_eq!(credentials.len(), 1);
335 assert_eq!(credentials[0].credential_id, credential_id);
336 }
337
338 #[tokio::test]
339 async fn test_get_credential() {
340 let user_repo = Arc::new(MockUserRepository::default());
341 let passkey_repo = Arc::new(MockPasskeyRepository::default());
342 let service = PasskeyService::new(user_repo, passkey_repo);
343
344 let user_id = UserId::new_random();
345 let credential_id = vec![1, 2, 3, 4];
346 let public_key = vec![5, 6, 7, 8];
347
348 service
350 .register_credential(&user_id, credential_id.clone(), public_key, None)
351 .await
352 .unwrap();
353
354 let result = service.get_credential(&credential_id).await;
356 assert!(result.is_ok());
357
358 let credential = result.unwrap();
359 assert!(credential.is_some());
360 assert_eq!(credential.unwrap().credential_id, credential_id);
361 }
362
363 #[tokio::test]
364 async fn test_get_credential_not_found() {
365 let user_repo = Arc::new(MockUserRepository::default());
366 let passkey_repo = Arc::new(MockPasskeyRepository::default());
367 let service = PasskeyService::new(user_repo, passkey_repo);
368
369 let result = service.get_credential(&[9, 9, 9]).await;
370 assert!(result.is_ok());
371 assert!(result.unwrap().is_none());
372 }
373
374 #[tokio::test]
375 async fn test_authenticate_credential() {
376 let user_repo = Arc::new(MockUserRepository::default());
377 let passkey_repo = Arc::new(MockPasskeyRepository::default());
378 let service = PasskeyService::new(user_repo.clone(), passkey_repo);
379
380 let credential_id = vec![1, 2, 3, 4];
381 let public_key = vec![5, 6, 7, 8];
382
383 let new_user = crate::storage::NewUser::builder()
385 .email("test@example.com".to_string())
386 .build()
387 .unwrap();
388 let user = user_repo.create(new_user).await.unwrap();
389 service
390 .register_credential(&user.id, credential_id.clone(), public_key, None)
391 .await
392 .unwrap();
393
394 let result = service.authenticate_credential(&credential_id).await;
396 assert!(result.is_ok());
397
398 let auth_user = result.unwrap();
399 assert!(auth_user.is_some());
400 assert_eq!(auth_user.unwrap().email, "test@example.com");
401 }
402
403 #[tokio::test]
404 async fn test_authenticate_credential_not_found() {
405 let user_repo = Arc::new(MockUserRepository::default());
406 let passkey_repo = Arc::new(MockPasskeyRepository::default());
407 let service = PasskeyService::new(user_repo, passkey_repo);
408
409 let result = service.authenticate_credential(&[9, 9, 9]).await;
410 assert!(result.is_ok());
411 assert!(result.unwrap().is_none());
412 }
413
414 #[tokio::test]
415 async fn test_delete_credential() {
416 let user_repo = Arc::new(MockUserRepository::default());
417 let passkey_repo = Arc::new(MockPasskeyRepository::default());
418 let service = PasskeyService::new(user_repo, passkey_repo);
419
420 let user_id = UserId::new_random();
421 let credential_id = vec![1, 2, 3, 4];
422 let public_key = vec![5, 6, 7, 8];
423
424 service
426 .register_credential(&user_id, credential_id.clone(), public_key, None)
427 .await
428 .unwrap();
429
430 let result = service.get_credential(&credential_id).await;
432 assert!(result.is_ok());
433 assert!(result.unwrap().is_some());
434
435 let result = service.delete_credential(&credential_id).await;
437 assert!(result.is_ok());
438
439 let result = service.get_credential(&credential_id).await;
441 assert!(result.is_ok());
442 assert!(result.unwrap().is_none());
443 }
444
445 #[tokio::test]
446 async fn test_delete_user_credentials() {
447 let user_repo = Arc::new(MockUserRepository::default());
448 let passkey_repo = Arc::new(MockPasskeyRepository::default());
449 let service = PasskeyService::new(user_repo, passkey_repo);
450
451 let user_id = UserId::new_random();
452 let credential_id1 = vec![1, 2, 3, 4];
453 let credential_id2 = vec![5, 6, 7, 8];
454 let public_key = vec![9, 10, 11, 12];
455
456 service
458 .register_credential(&user_id, credential_id1.clone(), public_key.clone(), None)
459 .await
460 .unwrap();
461 service
462 .register_credential(&user_id, credential_id2.clone(), public_key, None)
463 .await
464 .unwrap();
465
466 let result = service.get_user_credentials(&user_id).await;
468 assert!(result.is_ok());
469 assert_eq!(result.unwrap().len(), 2);
470
471 let result = service.delete_user_credentials(&user_id).await;
473 assert!(result.is_ok());
474
475 let result = service.get_user_credentials(&user_id).await;
477 assert!(result.is_ok());
478 assert_eq!(result.unwrap().len(), 0);
479 }
480}