Skip to main content

authx_storage/
ports.rs

1use async_trait::async_trait;
2use chrono::{DateTime, Utc};
3use uuid::Uuid;
4
5use authx_core::{
6    error::Result,
7    models::{
8        ApiKey, AuditLog, AuthorizationCode, CreateApiKey, CreateAuditLog, CreateAuthorizationCode,
9        CreateCredential, CreateDeviceCode, CreateInvite, CreateOidcClient,
10        CreateOidcFederationProvider, CreateOidcToken, CreateOrg, CreateSession, CreateUser,
11        Credential, CredentialKind, DeviceCode, Invite, Membership, OAuthAccount, OidcClient,
12        OidcFederationProvider, OidcToken, Organization, Role, Session, UpdateUser,
13        UpsertOAuthAccount, User,
14    },
15};
16
17#[async_trait]
18pub trait UserRepository: Send + Sync + 'static {
19    async fn find_by_id(&self, id: Uuid) -> Result<Option<User>>;
20    async fn find_by_email(&self, email: &str) -> Result<Option<User>>;
21    async fn find_by_username(&self, username: &str) -> Result<Option<User>>;
22    async fn list(&self, offset: u32, limit: u32) -> Result<Vec<User>>;
23    async fn create(&self, data: CreateUser) -> Result<User>;
24    async fn update(&self, id: Uuid, data: UpdateUser) -> Result<User>;
25    async fn delete(&self, id: Uuid) -> Result<()>;
26}
27
28#[async_trait]
29pub trait SessionRepository: Send + Sync + 'static {
30    async fn create(&self, data: CreateSession) -> Result<Session>;
31    async fn find_by_token_hash(&self, hash: &str) -> Result<Option<Session>>;
32    async fn find_by_user(&self, user_id: Uuid) -> Result<Vec<Session>>;
33    async fn invalidate(&self, session_id: Uuid) -> Result<()>;
34    async fn invalidate_all_for_user(&self, user_id: Uuid) -> Result<()>;
35    async fn set_org(&self, session_id: Uuid, org_id: Option<Uuid>) -> Result<Session>;
36}
37
38#[async_trait]
39pub trait CredentialRepository: Send + Sync + 'static {
40    async fn create(&self, data: CreateCredential) -> Result<Credential>;
41    async fn find_password_hash(&self, user_id: Uuid) -> Result<Option<String>>;
42    async fn find_by_user_and_kind(
43        &self,
44        user_id: Uuid,
45        kind: CredentialKind,
46    ) -> Result<Option<Credential>>;
47    async fn delete_by_user_and_kind(&self, user_id: Uuid, kind: CredentialKind) -> Result<()>;
48}
49
50#[async_trait]
51pub trait OrgRepository: Send + Sync + 'static {
52    async fn create(&self, data: CreateOrg) -> Result<Organization>;
53    async fn find_by_id(&self, id: Uuid) -> Result<Option<Organization>>;
54    async fn find_by_slug(&self, slug: &str) -> Result<Option<Organization>>;
55    async fn add_member(&self, org_id: Uuid, user_id: Uuid, role_id: Uuid) -> Result<Membership>;
56    async fn remove_member(&self, org_id: Uuid, user_id: Uuid) -> Result<()>;
57    async fn get_members(&self, org_id: Uuid) -> Result<Vec<Membership>>;
58    async fn find_roles(&self, org_id: Uuid) -> Result<Vec<Role>>;
59    async fn create_role(
60        &self,
61        org_id: Uuid,
62        name: String,
63        permissions: Vec<String>,
64    ) -> Result<Role>;
65    async fn update_member_role(
66        &self,
67        org_id: Uuid,
68        user_id: Uuid,
69        role_id: Uuid,
70    ) -> Result<Membership>;
71}
72
73#[async_trait]
74pub trait AuditLogRepository: Send + Sync + 'static {
75    async fn append(&self, entry: CreateAuditLog) -> Result<AuditLog>;
76    async fn find_by_user(&self, user_id: Uuid, limit: u32) -> Result<Vec<AuditLog>>;
77    async fn find_by_org(&self, org_id: Uuid, limit: u32) -> Result<Vec<AuditLog>>;
78}
79
80#[async_trait]
81pub trait ApiKeyRepository: Send + Sync + 'static {
82    async fn create(&self, data: CreateApiKey) -> Result<ApiKey>;
83    async fn find_by_hash(&self, key_hash: &str) -> Result<Option<ApiKey>>;
84    async fn find_by_user(&self, user_id: Uuid) -> Result<Vec<ApiKey>>;
85    async fn revoke(&self, key_id: Uuid, user_id: Uuid) -> Result<()>;
86    async fn touch_last_used(&self, key_id: Uuid, at: DateTime<Utc>) -> Result<()>;
87}
88
89#[async_trait]
90pub trait OAuthAccountRepository: Send + Sync + 'static {
91    async fn upsert(&self, data: UpsertOAuthAccount) -> Result<OAuthAccount>;
92    async fn find_by_provider(
93        &self,
94        provider: &str,
95        provider_user_id: &str,
96    ) -> Result<Option<OAuthAccount>>;
97    async fn find_by_user(&self, user_id: Uuid) -> Result<Vec<OAuthAccount>>;
98    async fn delete(&self, id: Uuid) -> Result<()>;
99}
100
101#[async_trait]
102pub trait InviteRepository: Send + Sync + 'static {
103    async fn create(&self, data: CreateInvite) -> Result<Invite>;
104    async fn find_by_token_hash(&self, hash: &str) -> Result<Option<Invite>>;
105    async fn accept(&self, invite_id: Uuid) -> Result<Invite>;
106    async fn delete_expired(&self) -> Result<u64>;
107}
108
109// ── OIDC Provider (authx as IdP) ───────────────────────────────────────────────
110
111#[async_trait]
112pub trait OidcClientRepository: Send + Sync + 'static {
113    async fn create(&self, data: CreateOidcClient) -> Result<OidcClient>;
114    async fn find_by_client_id(&self, client_id: &str) -> Result<Option<OidcClient>>;
115    async fn list(&self, offset: u32, limit: u32) -> Result<Vec<OidcClient>>;
116}
117
118#[async_trait]
119pub trait AuthorizationCodeRepository: Send + Sync + 'static {
120    async fn create(&self, data: CreateAuthorizationCode) -> Result<AuthorizationCode>;
121    async fn find_by_code_hash(&self, hash: &str) -> Result<Option<AuthorizationCode>>;
122    async fn mark_used(&self, id: Uuid) -> Result<()>;
123    async fn delete_expired(&self) -> Result<u64>;
124}
125
126#[async_trait]
127pub trait OidcTokenRepository: Send + Sync + 'static {
128    async fn create(&self, data: CreateOidcToken) -> Result<OidcToken>;
129    async fn find_by_token_hash(&self, hash: &str) -> Result<Option<OidcToken>>;
130    async fn revoke(&self, id: Uuid) -> Result<()>;
131    async fn revoke_all_for_user_client(&self, user_id: Uuid, client_id: &str) -> Result<()>;
132}
133
134#[async_trait]
135pub trait OidcFederationProviderRepository: Send + Sync + 'static {
136    async fn create(&self, data: CreateOidcFederationProvider) -> Result<OidcFederationProvider>;
137    async fn find_by_id(&self, id: Uuid) -> Result<Option<OidcFederationProvider>>;
138    async fn find_by_name(&self, name: &str) -> Result<Option<OidcFederationProvider>>;
139    async fn list_enabled(&self) -> Result<Vec<OidcFederationProvider>>;
140}
141
142// ── Device Authorization Grant (RFC 8628) ────────────────────────────────────
143
144#[async_trait]
145pub trait DeviceCodeRepository: Send + Sync + 'static {
146    async fn create(&self, data: CreateDeviceCode) -> Result<DeviceCode>;
147    async fn find_by_device_code_hash(&self, hash: &str) -> Result<Option<DeviceCode>>;
148    async fn find_by_user_code_hash(&self, hash: &str) -> Result<Option<DeviceCode>>;
149    async fn authorize(&self, id: Uuid, user_id: Uuid) -> Result<()>;
150    async fn deny(&self, id: Uuid) -> Result<()>;
151    async fn update_last_polled(&self, id: Uuid, interval_secs: u32) -> Result<()>;
152    async fn delete(&self, id: Uuid) -> Result<()>;
153    async fn delete_expired(&self) -> Result<u64>;
154    async fn list_by_client(
155        &self,
156        client_id: &str,
157        offset: u32,
158        limit: u32,
159    ) -> Result<Vec<DeviceCode>>;
160}
161
162/// Composite adapter trait — storage backends implement this.
163pub trait StorageAdapter:
164    UserRepository
165    + SessionRepository
166    + CredentialRepository
167    + OrgRepository
168    + AuditLogRepository
169    + ApiKeyRepository
170    + OAuthAccountRepository
171    + InviteRepository
172    + OidcClientRepository
173    + AuthorizationCodeRepository
174    + OidcTokenRepository
175    + OidcFederationProviderRepository
176    + DeviceCodeRepository
177    + Clone
178    + Send
179    + Sync
180    + 'static
181{
182}
183
184impl<T> StorageAdapter for T where
185    T: UserRepository
186        + SessionRepository
187        + CredentialRepository
188        + OrgRepository
189        + AuditLogRepository
190        + ApiKeyRepository
191        + OAuthAccountRepository
192        + InviteRepository
193        + OidcClientRepository
194        + AuthorizationCodeRepository
195        + OidcTokenRepository
196        + OidcFederationProviderRepository
197        + DeviceCodeRepository
198        + Clone
199        + Send
200        + Sync
201        + 'static
202{
203}