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