Skip to main content

better_auth_core/adapters/
memory.rs

1use async_trait::async_trait;
2use chrono::{DateTime, Utc};
3use std::collections::HashMap;
4use std::sync::{Arc, Mutex};
5use uuid::Uuid;
6
7use crate::error::{AuthError, AuthResult};
8use crate::types::{
9    Account, CreateAccount, CreateInvitation, CreateMember, CreateOrganization, CreateSession,
10    CreateTwoFactor, CreateUser, CreateVerification, Invitation, InvitationStatus, Member,
11    Organization, Session, TwoFactor, UpdateAccount, UpdateOrganization, UpdateUser, User,
12    Verification,
13};
14
15pub use super::memory_traits::{
16    MemoryAccount, MemoryInvitation, MemoryMember, MemoryOrganization, MemorySession,
17    MemoryTwoFactor, MemoryUser, MemoryVerification,
18};
19
20use super::traits::{
21    AccountOps, InvitationOps, MemberOps, OrganizationOps, SessionOps, TwoFactorOps, UserOps,
22    VerificationOps,
23};
24
25/// In-memory database adapter for testing and development.
26///
27/// Generic over entity types — use default type parameters for the built-in
28/// types, or supply your own custom structs that implement the `Memory*`
29/// traits.
30///
31/// ```rust,ignore
32/// // Using built-in types (no turbofish needed):
33/// let adapter = MemoryDatabaseAdapter::new();
34///
35/// // Using custom types:
36/// let adapter = MemoryDatabaseAdapter::<MyUser, MySession, MyAccount,
37///     MyOrg, MyMember, MyInvitation, MyVerification>::new();
38/// ```
39pub struct MemoryDatabaseAdapter<
40    U = User,
41    S = Session,
42    A = Account,
43    O = Organization,
44    M = Member,
45    I = Invitation,
46    V = Verification,
47> {
48    users: Arc<Mutex<HashMap<String, U>>>,
49    sessions: Arc<Mutex<HashMap<String, S>>>,
50    accounts: Arc<Mutex<HashMap<String, A>>>,
51    verifications: Arc<Mutex<HashMap<String, V>>>,
52    email_index: Arc<Mutex<HashMap<String, String>>>,
53    username_index: Arc<Mutex<HashMap<String, String>>>,
54    organizations: Arc<Mutex<HashMap<String, O>>>,
55    members: Arc<Mutex<HashMap<String, M>>>,
56    invitations: Arc<Mutex<HashMap<String, I>>>,
57    slug_index: Arc<Mutex<HashMap<String, String>>>,
58    two_factors: Arc<Mutex<HashMap<String, TwoFactor>>>,
59}
60
61/// Constructor for the default (built-in) entity types.
62/// Use `Default::default()` for custom type parameterizations.
63impl MemoryDatabaseAdapter {
64    pub fn new() -> Self {
65        Self::default()
66    }
67}
68
69impl<U, S, A, O, M, I, V> Default for MemoryDatabaseAdapter<U, S, A, O, M, I, V> {
70    fn default() -> Self {
71        Self {
72            users: Arc::new(Mutex::new(HashMap::new())),
73            sessions: Arc::new(Mutex::new(HashMap::new())),
74            accounts: Arc::new(Mutex::new(HashMap::new())),
75            verifications: Arc::new(Mutex::new(HashMap::new())),
76            email_index: Arc::new(Mutex::new(HashMap::new())),
77            username_index: Arc::new(Mutex::new(HashMap::new())),
78            organizations: Arc::new(Mutex::new(HashMap::new())),
79            members: Arc::new(Mutex::new(HashMap::new())),
80            invitations: Arc::new(Mutex::new(HashMap::new())),
81            slug_index: Arc::new(Mutex::new(HashMap::new())),
82            two_factors: Arc::new(Mutex::new(HashMap::new())),
83        }
84    }
85}
86
87// -- UserOps --
88
89#[async_trait]
90impl<U, S, A, O, M, I, V> UserOps for MemoryDatabaseAdapter<U, S, A, O, M, I, V>
91where
92    U: MemoryUser,
93    S: MemorySession,
94    A: MemoryAccount,
95    O: MemoryOrganization,
96    M: MemoryMember,
97    I: MemoryInvitation,
98    V: MemoryVerification,
99{
100    type User = U;
101
102    async fn create_user(&self, create_user: CreateUser) -> AuthResult<U> {
103        let mut users = self.users.lock().unwrap();
104        let mut email_index = self.email_index.lock().unwrap();
105        let mut username_index = self.username_index.lock().unwrap();
106
107        let id = create_user
108            .id
109            .clone()
110            .unwrap_or_else(|| Uuid::new_v4().to_string());
111
112        if let Some(email) = &create_user.email
113            && email_index.contains_key(email)
114        {
115            return Err(AuthError::config("Email already exists"));
116        }
117
118        if let Some(username) = &create_user.username
119            && username_index.contains_key(username)
120        {
121            return Err(AuthError::conflict(
122                "A user with this username already exists",
123            ));
124        }
125
126        let now = Utc::now();
127        let user = U::from_create(id.clone(), &create_user, now);
128
129        users.insert(id.clone(), user.clone());
130
131        if let Some(email) = &create_user.email {
132            email_index.insert(email.clone(), id.clone());
133        }
134        if let Some(username) = &create_user.username {
135            username_index.insert(username.clone(), id);
136        }
137
138        Ok(user)
139    }
140
141    async fn get_user_by_id(&self, id: &str) -> AuthResult<Option<U>> {
142        let users = self.users.lock().unwrap();
143        Ok(users.get(id).cloned())
144    }
145
146    async fn get_user_by_email(&self, email: &str) -> AuthResult<Option<U>> {
147        let email_index = self.email_index.lock().unwrap();
148        let users = self.users.lock().unwrap();
149
150        if let Some(user_id) = email_index.get(email) {
151            Ok(users.get(user_id).cloned())
152        } else {
153            Ok(None)
154        }
155    }
156
157    async fn get_user_by_username(&self, username: &str) -> AuthResult<Option<U>> {
158        let username_index = self.username_index.lock().unwrap();
159        let users = self.users.lock().unwrap();
160
161        if let Some(user_id) = username_index.get(username) {
162            Ok(users.get(user_id).cloned())
163        } else {
164            Ok(None)
165        }
166    }
167
168    async fn update_user(&self, id: &str, update: UpdateUser) -> AuthResult<U> {
169        let mut users = self.users.lock().unwrap();
170        let mut email_index = self.email_index.lock().unwrap();
171        let mut username_index = self.username_index.lock().unwrap();
172
173        let user = users.get_mut(id).ok_or(AuthError::UserNotFound)?;
174
175        // Update indices BEFORE mutation (read old values via trait getters)
176        if let Some(new_email) = &update.email {
177            if let Some(old_email) = user.email() {
178                email_index.remove(old_email);
179            }
180            email_index.insert(new_email.clone(), id.to_string());
181        }
182
183        if let Some(ref new_username) = update.username {
184            if let Some(old_username) = user.username() {
185                username_index.remove(old_username);
186            }
187            username_index.insert(new_username.clone(), id.to_string());
188        }
189
190        user.apply_update(&update);
191        Ok(user.clone())
192    }
193
194    async fn delete_user(&self, id: &str) -> AuthResult<()> {
195        let mut users = self.users.lock().unwrap();
196        let mut email_index = self.email_index.lock().unwrap();
197        let mut username_index = self.username_index.lock().unwrap();
198
199        if let Some(user) = users.remove(id) {
200            if let Some(email) = user.email() {
201                email_index.remove(email);
202            }
203            if let Some(username) = user.username() {
204                username_index.remove(username);
205            }
206        }
207
208        Ok(())
209    }
210}
211
212// -- SessionOps --
213
214#[async_trait]
215impl<U, S, A, O, M, I, V> SessionOps for MemoryDatabaseAdapter<U, S, A, O, M, I, V>
216where
217    U: MemoryUser,
218    S: MemorySession,
219    A: MemoryAccount,
220    O: MemoryOrganization,
221    M: MemoryMember,
222    I: MemoryInvitation,
223    V: MemoryVerification,
224{
225    type Session = S;
226
227    async fn create_session(&self, create_session: CreateSession) -> AuthResult<S> {
228        let mut sessions = self.sessions.lock().unwrap();
229
230        let id = Uuid::new_v4().to_string();
231        let token = format!("session_{}", Uuid::new_v4());
232        let now = Utc::now();
233        let session = S::from_create(id, token.clone(), &create_session, now);
234
235        sessions.insert(token, session.clone());
236        Ok(session)
237    }
238
239    async fn get_session(&self, token: &str) -> AuthResult<Option<S>> {
240        let sessions = self.sessions.lock().unwrap();
241        Ok(sessions.get(token).cloned())
242    }
243
244    async fn get_user_sessions(&self, user_id: &str) -> AuthResult<Vec<S>> {
245        let sessions = self.sessions.lock().unwrap();
246        Ok(sessions
247            .values()
248            .filter(|s| s.user_id() == user_id && s.active())
249            .cloned()
250            .collect())
251    }
252
253    async fn update_session_expiry(
254        &self,
255        token: &str,
256        expires_at: DateTime<Utc>,
257    ) -> AuthResult<()> {
258        let mut sessions = self.sessions.lock().unwrap();
259        if let Some(session) = sessions.get_mut(token) {
260            session.set_expires_at(expires_at);
261        }
262        Ok(())
263    }
264
265    async fn delete_session(&self, token: &str) -> AuthResult<()> {
266        let mut sessions = self.sessions.lock().unwrap();
267        sessions.remove(token);
268        Ok(())
269    }
270
271    async fn delete_user_sessions(&self, user_id: &str) -> AuthResult<()> {
272        let mut sessions = self.sessions.lock().unwrap();
273        sessions.retain(|_, s| s.user_id() != user_id);
274        Ok(())
275    }
276
277    async fn delete_expired_sessions(&self) -> AuthResult<usize> {
278        let mut sessions = self.sessions.lock().unwrap();
279        let now = Utc::now();
280        let initial_count = sessions.len();
281        sessions.retain(|_, s| s.expires_at() > now && s.active());
282        Ok(initial_count - sessions.len())
283    }
284
285    async fn update_session_active_organization(
286        &self,
287        token: &str,
288        organization_id: Option<&str>,
289    ) -> AuthResult<S> {
290        let mut sessions = self.sessions.lock().unwrap();
291        let session = sessions.get_mut(token).ok_or(AuthError::SessionNotFound)?;
292        session.set_active_organization_id(organization_id.map(|s| s.to_string()));
293        session.set_updated_at(Utc::now());
294        Ok(session.clone())
295    }
296}
297
298// -- AccountOps --
299
300#[async_trait]
301impl<U, S, A, O, M, I, V> AccountOps for MemoryDatabaseAdapter<U, S, A, O, M, I, V>
302where
303    U: MemoryUser,
304    S: MemorySession,
305    A: MemoryAccount,
306    O: MemoryOrganization,
307    M: MemoryMember,
308    I: MemoryInvitation,
309    V: MemoryVerification,
310{
311    type Account = A;
312
313    async fn create_account(&self, create_account: CreateAccount) -> AuthResult<A> {
314        let mut accounts = self.accounts.lock().unwrap();
315
316        let id = Uuid::new_v4().to_string();
317        let now = Utc::now();
318        let account = A::from_create(id.clone(), &create_account, now);
319
320        accounts.insert(id, account.clone());
321        Ok(account)
322    }
323
324    async fn get_account(
325        &self,
326        provider: &str,
327        provider_account_id: &str,
328    ) -> AuthResult<Option<A>> {
329        let accounts = self.accounts.lock().unwrap();
330        Ok(accounts
331            .values()
332            .find(|acc| acc.provider_id() == provider && acc.account_id() == provider_account_id)
333            .cloned())
334    }
335
336    async fn get_user_accounts(&self, user_id: &str) -> AuthResult<Vec<A>> {
337        let accounts = self.accounts.lock().unwrap();
338        Ok(accounts
339            .values()
340            .filter(|acc| acc.user_id() == user_id)
341            .cloned()
342            .collect())
343    }
344
345    async fn update_account(&self, id: &str, update: UpdateAccount) -> AuthResult<A> {
346        let mut accounts = self.accounts.lock().unwrap();
347        let account = accounts
348            .get_mut(id)
349            .ok_or_else(|| AuthError::not_found("Account not found"))?;
350        account.apply_update(&update);
351        Ok(account.clone())
352    }
353
354    async fn delete_account(&self, id: &str) -> AuthResult<()> {
355        let mut accounts = self.accounts.lock().unwrap();
356        accounts.remove(id);
357        Ok(())
358    }
359}
360
361// -- VerificationOps --
362
363#[async_trait]
364impl<U, S, A, O, M, I, V> VerificationOps for MemoryDatabaseAdapter<U, S, A, O, M, I, V>
365where
366    U: MemoryUser,
367    S: MemorySession,
368    A: MemoryAccount,
369    O: MemoryOrganization,
370    M: MemoryMember,
371    I: MemoryInvitation,
372    V: MemoryVerification,
373{
374    type Verification = V;
375
376    async fn create_verification(&self, create_verification: CreateVerification) -> AuthResult<V> {
377        let mut verifications = self.verifications.lock().unwrap();
378
379        let id = Uuid::new_v4().to_string();
380        let now = Utc::now();
381        let verification = V::from_create(id.clone(), &create_verification, now);
382
383        verifications.insert(id, verification.clone());
384        Ok(verification)
385    }
386
387    async fn get_verification(&self, identifier: &str, value: &str) -> AuthResult<Option<V>> {
388        let verifications = self.verifications.lock().unwrap();
389        let now = Utc::now();
390        Ok(verifications
391            .values()
392            .find(|v| v.identifier() == identifier && v.value() == value && v.expires_at() > now)
393            .cloned())
394    }
395
396    async fn get_verification_by_value(&self, value: &str) -> AuthResult<Option<V>> {
397        let verifications = self.verifications.lock().unwrap();
398        let now = Utc::now();
399        Ok(verifications
400            .values()
401            .find(|v| v.value() == value && v.expires_at() > now)
402            .cloned())
403    }
404
405    async fn get_verification_by_identifier(&self, identifier: &str) -> AuthResult<Option<V>> {
406        let verifications = self.verifications.lock().unwrap();
407        let now = Utc::now();
408        Ok(verifications
409            .values()
410            .find(|v| v.identifier() == identifier && v.expires_at() > now)
411            .cloned())
412    }
413
414    async fn delete_verification(&self, id: &str) -> AuthResult<()> {
415        let mut verifications = self.verifications.lock().unwrap();
416        verifications.remove(id);
417        Ok(())
418    }
419
420    async fn delete_expired_verifications(&self) -> AuthResult<usize> {
421        let mut verifications = self.verifications.lock().unwrap();
422        let now = Utc::now();
423        let initial_count = verifications.len();
424        verifications.retain(|_, v| v.expires_at() > now);
425        Ok(initial_count - verifications.len())
426    }
427}
428
429// -- OrganizationOps --
430
431#[async_trait]
432impl<U, S, A, O, M, I, V> OrganizationOps for MemoryDatabaseAdapter<U, S, A, O, M, I, V>
433where
434    U: MemoryUser,
435    S: MemorySession,
436    A: MemoryAccount,
437    O: MemoryOrganization,
438    M: MemoryMember,
439    I: MemoryInvitation,
440    V: MemoryVerification,
441{
442    type Organization = O;
443
444    async fn create_organization(&self, create_org: CreateOrganization) -> AuthResult<O> {
445        let mut organizations = self.organizations.lock().unwrap();
446        let mut slug_index = self.slug_index.lock().unwrap();
447
448        if slug_index.contains_key(&create_org.slug) {
449            return Err(AuthError::conflict("Organization slug already exists"));
450        }
451
452        let id = create_org
453            .id
454            .clone()
455            .unwrap_or_else(|| Uuid::new_v4().to_string());
456        let now = Utc::now();
457        let organization = O::from_create(id.clone(), &create_org, now);
458
459        organizations.insert(id.clone(), organization.clone());
460        slug_index.insert(create_org.slug.clone(), id);
461
462        Ok(organization)
463    }
464
465    async fn get_organization_by_id(&self, id: &str) -> AuthResult<Option<O>> {
466        let organizations = self.organizations.lock().unwrap();
467        Ok(organizations.get(id).cloned())
468    }
469
470    async fn get_organization_by_slug(&self, slug: &str) -> AuthResult<Option<O>> {
471        let slug_index = self.slug_index.lock().unwrap();
472        let organizations = self.organizations.lock().unwrap();
473
474        if let Some(org_id) = slug_index.get(slug) {
475            Ok(organizations.get(org_id).cloned())
476        } else {
477            Ok(None)
478        }
479    }
480
481    async fn update_organization(&self, id: &str, update: UpdateOrganization) -> AuthResult<O> {
482        let mut organizations = self.organizations.lock().unwrap();
483        let mut slug_index = self.slug_index.lock().unwrap();
484
485        let org = organizations
486            .get_mut(id)
487            .ok_or_else(|| AuthError::not_found("Organization not found"))?;
488
489        // Update slug index BEFORE mutation
490        if let Some(new_slug) = &update.slug {
491            let current_slug = org.slug().to_string();
492            if *new_slug != current_slug {
493                if slug_index.contains_key(new_slug.as_str()) {
494                    return Err(AuthError::conflict("Organization slug already exists"));
495                }
496                slug_index.remove(&current_slug);
497                slug_index.insert(new_slug.clone(), id.to_string());
498            }
499        }
500
501        org.apply_update(&update);
502        Ok(org.clone())
503    }
504
505    async fn delete_organization(&self, id: &str) -> AuthResult<()> {
506        let mut organizations = self.organizations.lock().unwrap();
507        let mut slug_index = self.slug_index.lock().unwrap();
508        let mut members = self.members.lock().unwrap();
509        let mut invitations = self.invitations.lock().unwrap();
510
511        if let Some(org) = organizations.remove(id) {
512            slug_index.remove(org.slug());
513        }
514
515        members.retain(|_, m| m.organization_id() != id);
516        invitations.retain(|_, i| i.organization_id() != id);
517
518        Ok(())
519    }
520
521    async fn list_user_organizations(&self, user_id: &str) -> AuthResult<Vec<O>> {
522        let members = self.members.lock().unwrap();
523        let organizations = self.organizations.lock().unwrap();
524
525        let org_ids: Vec<String> = members
526            .values()
527            .filter(|m| m.user_id() == user_id)
528            .map(|m| m.organization_id().to_string())
529            .collect();
530
531        let orgs = org_ids
532            .iter()
533            .filter_map(|id| organizations.get(id).cloned())
534            .collect();
535
536        Ok(orgs)
537    }
538}
539
540// -- MemberOps --
541
542#[async_trait]
543impl<U, S, A, O, M, I, V> MemberOps for MemoryDatabaseAdapter<U, S, A, O, M, I, V>
544where
545    U: MemoryUser,
546    S: MemorySession,
547    A: MemoryAccount,
548    O: MemoryOrganization,
549    M: MemoryMember,
550    I: MemoryInvitation,
551    V: MemoryVerification,
552{
553    type Member = M;
554
555    async fn create_member(&self, create_member: CreateMember) -> AuthResult<M> {
556        let mut members = self.members.lock().unwrap();
557
558        let exists = members.values().any(|m| {
559            m.organization_id() == create_member.organization_id
560                && m.user_id() == create_member.user_id
561        });
562
563        if exists {
564            return Err(AuthError::conflict(
565                "User is already a member of this organization",
566            ));
567        }
568
569        let id = Uuid::new_v4().to_string();
570        let now = Utc::now();
571        let member = M::from_create(id.clone(), &create_member, now);
572
573        members.insert(id, member.clone());
574        Ok(member)
575    }
576
577    async fn get_member(&self, organization_id: &str, user_id: &str) -> AuthResult<Option<M>> {
578        let members = self.members.lock().unwrap();
579        Ok(members
580            .values()
581            .find(|m| m.organization_id() == organization_id && m.user_id() == user_id)
582            .cloned())
583    }
584
585    async fn get_member_by_id(&self, id: &str) -> AuthResult<Option<M>> {
586        let members = self.members.lock().unwrap();
587        Ok(members.get(id).cloned())
588    }
589
590    async fn update_member_role(&self, member_id: &str, role: &str) -> AuthResult<M> {
591        let mut members = self.members.lock().unwrap();
592        let member = members
593            .get_mut(member_id)
594            .ok_or_else(|| AuthError::not_found("Member not found"))?;
595        member.set_role(role.to_string());
596        Ok(member.clone())
597    }
598
599    async fn delete_member(&self, member_id: &str) -> AuthResult<()> {
600        let mut members = self.members.lock().unwrap();
601        members.remove(member_id);
602        Ok(())
603    }
604
605    async fn list_organization_members(&self, organization_id: &str) -> AuthResult<Vec<M>> {
606        let members = self.members.lock().unwrap();
607        Ok(members
608            .values()
609            .filter(|m| m.organization_id() == organization_id)
610            .cloned()
611            .collect())
612    }
613
614    async fn count_organization_members(&self, organization_id: &str) -> AuthResult<usize> {
615        let members = self.members.lock().unwrap();
616        Ok(members
617            .values()
618            .filter(|m| m.organization_id() == organization_id)
619            .count())
620    }
621
622    async fn count_organization_owners(&self, organization_id: &str) -> AuthResult<usize> {
623        let members = self.members.lock().unwrap();
624        Ok(members
625            .values()
626            .filter(|m| m.organization_id() == organization_id && m.role() == "owner")
627            .count())
628    }
629}
630
631// -- InvitationOps --
632
633#[async_trait]
634impl<U, S, A, O, M, I, V> InvitationOps for MemoryDatabaseAdapter<U, S, A, O, M, I, V>
635where
636    U: MemoryUser,
637    S: MemorySession,
638    A: MemoryAccount,
639    O: MemoryOrganization,
640    M: MemoryMember,
641    I: MemoryInvitation,
642    V: MemoryVerification,
643{
644    type Invitation = I;
645
646    async fn create_invitation(&self, create_inv: CreateInvitation) -> AuthResult<I> {
647        let mut invitations = self.invitations.lock().unwrap();
648
649        let id = Uuid::new_v4().to_string();
650        let now = Utc::now();
651        let invitation = I::from_create(id.clone(), &create_inv, now);
652
653        invitations.insert(id, invitation.clone());
654        Ok(invitation)
655    }
656
657    async fn get_invitation_by_id(&self, id: &str) -> AuthResult<Option<I>> {
658        let invitations = self.invitations.lock().unwrap();
659        Ok(invitations.get(id).cloned())
660    }
661
662    async fn get_pending_invitation(
663        &self,
664        organization_id: &str,
665        email: &str,
666    ) -> AuthResult<Option<I>> {
667        let invitations = self.invitations.lock().unwrap();
668        Ok(invitations
669            .values()
670            .find(|i| {
671                i.organization_id() == organization_id
672                    && i.email().to_lowercase() == email.to_lowercase()
673                    && *i.status() == InvitationStatus::Pending
674            })
675            .cloned())
676    }
677
678    async fn update_invitation_status(&self, id: &str, status: InvitationStatus) -> AuthResult<I> {
679        let mut invitations = self.invitations.lock().unwrap();
680        let invitation = invitations
681            .get_mut(id)
682            .ok_or_else(|| AuthError::not_found("Invitation not found"))?;
683        invitation.set_status(status);
684        Ok(invitation.clone())
685    }
686
687    async fn list_organization_invitations(&self, organization_id: &str) -> AuthResult<Vec<I>> {
688        let invitations = self.invitations.lock().unwrap();
689        Ok(invitations
690            .values()
691            .filter(|i| i.organization_id() == organization_id)
692            .cloned()
693            .collect())
694    }
695
696    async fn list_user_invitations(&self, email: &str) -> AuthResult<Vec<I>> {
697        let invitations = self.invitations.lock().unwrap();
698        let now = Utc::now();
699        Ok(invitations
700            .values()
701            .filter(|i| {
702                i.email().to_lowercase() == email.to_lowercase()
703                    && *i.status() == InvitationStatus::Pending
704                    && i.expires_at() > now
705            })
706            .cloned()
707            .collect())
708    }
709}
710
711// -- TwoFactorOps --
712
713#[async_trait]
714impl<U, S, A, O, M, I, V> TwoFactorOps for MemoryDatabaseAdapter<U, S, A, O, M, I, V>
715where
716    U: MemoryUser,
717    S: MemorySession,
718    A: MemoryAccount,
719    O: MemoryOrganization,
720    M: MemoryMember,
721    I: MemoryInvitation,
722    V: MemoryVerification,
723{
724    type TwoFactor = TwoFactor;
725
726    async fn create_two_factor(&self, create: CreateTwoFactor) -> AuthResult<TwoFactor> {
727        let mut two_factors = self.two_factors.lock().unwrap();
728
729        // Check if user already has 2FA
730        if two_factors.values().any(|tf| tf.user_id == create.user_id) {
731            return Err(AuthError::conflict(
732                "Two-factor authentication already enabled for this user",
733            ));
734        }
735
736        let id = Uuid::new_v4().to_string();
737        let now = Utc::now();
738        let two_factor: TwoFactor = MemoryTwoFactor::from_create(id.clone(), &create, now);
739
740        two_factors.insert(id, two_factor.clone());
741        Ok(two_factor)
742    }
743
744    async fn get_two_factor_by_user_id(&self, user_id: &str) -> AuthResult<Option<TwoFactor>> {
745        let two_factors = self.two_factors.lock().unwrap();
746        Ok(two_factors
747            .values()
748            .find(|tf| tf.user_id == user_id)
749            .cloned())
750    }
751
752    async fn update_two_factor_backup_codes(
753        &self,
754        user_id: &str,
755        backup_codes: &str,
756    ) -> AuthResult<TwoFactor> {
757        let mut two_factors = self.two_factors.lock().unwrap();
758        let two_factor = two_factors
759            .values_mut()
760            .find(|tf| tf.user_id == user_id)
761            .ok_or_else(|| AuthError::not_found("Two-factor record not found"))?;
762        two_factor.set_backup_codes(backup_codes.to_string());
763        Ok(two_factor.clone())
764    }
765
766    async fn delete_two_factor(&self, user_id: &str) -> AuthResult<()> {
767        let mut two_factors = self.two_factors.lock().unwrap();
768        two_factors.retain(|_, tf| tf.user_id != user_id);
769        Ok(())
770    }
771}