better_auth/
types.rs

1use serde::{Deserialize, Serialize};
2use chrono::{DateTime, Utc};
3use uuid::Uuid;
4use std::collections::HashMap;
5
6
7
8/// Core user type - matches OpenAPI schema
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
10pub struct User {
11    pub id: String,
12    pub name: Option<String>,
13    pub email: Option<String>,
14    #[serde(rename = "emailVerified")]
15    pub email_verified: bool,
16    pub image: Option<String>,
17    #[serde(rename = "createdAt")]
18    pub created_at: DateTime<Utc>,
19    #[serde(rename = "updatedAt")]
20    pub updated_at: DateTime<Utc>,
21    pub username: Option<String>,
22    #[serde(rename = "displayUsername")]
23    pub display_username: Option<String>,
24    #[serde(rename = "twoFactorEnabled")]
25    pub two_factor_enabled: bool,
26    pub role: Option<String>,
27    pub banned: bool,
28    #[serde(rename = "banReason")]
29    pub ban_reason: Option<String>,
30    #[serde(rename = "banExpires")]
31    pub ban_expires: Option<DateTime<Utc>>,
32    // Keep metadata for internal use but don't serialize
33    #[serde(skip)]
34    pub metadata: HashMap<String, serde_json::Value>,
35}
36
37/// Session information - matches OpenAPI schema
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct Session {
40    pub id: String,
41    #[serde(rename = "expiresAt")]
42    pub expires_at: DateTime<Utc>,
43    pub token: String,
44    #[serde(rename = "createdAt")]
45    pub created_at: DateTime<Utc>,
46    #[serde(rename = "updatedAt")]
47    pub updated_at: DateTime<Utc>,
48    #[serde(rename = "ipAddress")]
49    pub ip_address: Option<String>,
50    #[serde(rename = "userAgent")]  
51    pub user_agent: Option<String>,
52    #[serde(rename = "userId")]
53    pub user_id: String,
54    #[serde(rename = "impersonatedBy")]
55    pub impersonated_by: Option<String>,
56    #[serde(rename = "activeOrganizationId")]
57    pub active_organization_id: Option<String>,
58    // Keep active field for internal use but don't serialize
59    #[serde(skip)]
60    pub active: bool,
61}
62
63/// Account linking (for OAuth providers) - matches OpenAPI schema
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct Account {
66    pub id: String,
67    #[serde(rename = "accountId")]
68    pub account_id: String,
69    #[serde(rename = "providerId")]
70    pub provider_id: String,
71    #[serde(rename = "userId")]
72    pub user_id: String,
73    #[serde(rename = "accessToken")]
74    pub access_token: Option<String>,
75    #[serde(rename = "refreshToken")]
76    pub refresh_token: Option<String>,
77    #[serde(rename = "idToken")]
78    pub id_token: Option<String>,
79    #[serde(rename = "accessTokenExpiresAt")]
80    pub access_token_expires_at: Option<DateTime<Utc>>,
81    #[serde(rename = "refreshTokenExpiresAt")] 
82    pub refresh_token_expires_at: Option<DateTime<Utc>>,
83    pub scope: Option<String>,
84    pub password: Option<String>,
85    #[serde(rename = "createdAt")]
86    pub created_at: DateTime<Utc>,
87    #[serde(rename = "updatedAt")]
88    pub updated_at: DateTime<Utc>,
89}
90
91/// Verification token - matches OpenAPI schema
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct Verification {
94    pub id: String,
95    pub identifier: String,
96    pub value: String,
97    #[serde(rename = "expiresAt")]
98    pub expires_at: DateTime<Utc>,
99    #[serde(rename = "createdAt")]
100    pub created_at: DateTime<Utc>,
101    #[serde(rename = "updatedAt")]
102    pub updated_at: DateTime<Utc>,
103}
104
105/// Two-factor authentication - matches OpenAPI schema
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct TwoFactor {
108    pub id: String,
109    pub secret: String,
110    #[serde(rename = "backupCodes")]
111    pub backup_codes: Option<String>,
112    #[serde(rename = "userId")]
113    pub user_id: String,
114}
115
116/// Passkey authentication - matches OpenAPI schema
117#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct Passkey {
119    pub id: String,
120    pub name: String,
121    #[serde(rename = "publicKey")]
122    pub public_key: String,
123    #[serde(rename = "userId")]
124    pub user_id: String,
125    #[serde(rename = "credentialID")]
126    pub credential_id: String,
127    pub counter: u64,
128    #[serde(rename = "deviceType")]
129    pub device_type: String,
130    #[serde(rename = "backedUp")]
131    pub backed_up: bool,
132}
133
134/// HTTP method enumeration
135#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
136pub enum HttpMethod {
137    Get,
138    Post,
139    Put,
140    Delete,
141    Patch,
142    Options,
143    Head,
144}
145
146/// Authentication request wrapper
147#[derive(Debug, Clone)]
148pub struct AuthRequest {
149    pub method: HttpMethod,
150    pub path: String,
151    pub headers: HashMap<String, String>,
152    pub body: Option<Vec<u8>>,
153    pub query: HashMap<String, String>,
154}
155
156/// Authentication response wrapper
157#[derive(Debug, Clone)]
158pub struct AuthResponse {
159    pub status: u16,
160    pub headers: HashMap<String, String>,
161    pub body: Vec<u8>,
162}
163
164/// User creation data
165#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct CreateUser {
167    pub id: Option<String>,
168    pub email: Option<String>,
169    pub name: Option<String>,
170    pub image: Option<String>,
171    pub email_verified: Option<bool>,
172    pub password: Option<String>,
173    pub username: Option<String>,
174    pub display_username: Option<String>,
175    pub role: Option<String>,
176    pub metadata: Option<HashMap<String, serde_json::Value>>,
177}
178
179/// User update data
180#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct UpdateUser {
182    pub email: Option<String>,
183    pub name: Option<String>,
184    pub image: Option<String>,
185    pub email_verified: Option<bool>,
186    pub username: Option<String>,
187    pub display_username: Option<String>,
188    pub role: Option<String>,
189    pub banned: Option<bool>,
190    pub ban_reason: Option<String>,
191    pub ban_expires: Option<DateTime<Utc>>,
192    pub two_factor_enabled: Option<bool>,
193    pub metadata: Option<HashMap<String, serde_json::Value>>,
194}
195
196/// Session creation data
197#[derive(Debug, Clone)]
198pub struct CreateSession {
199    pub user_id: String,
200    pub expires_at: DateTime<Utc>,
201    pub ip_address: Option<String>,
202    pub user_agent: Option<String>,
203    pub impersonated_by: Option<String>,
204    pub active_organization_id: Option<String>,
205}
206
207/// Account creation data
208#[derive(Debug, Clone)]
209pub struct CreateAccount {
210    pub user_id: String,
211    pub account_id: String,
212    pub provider_id: String,
213    pub access_token: Option<String>,
214    pub refresh_token: Option<String>,
215    pub id_token: Option<String>,
216    pub access_token_expires_at: Option<DateTime<Utc>>,
217    pub refresh_token_expires_at: Option<DateTime<Utc>>,
218    pub scope: Option<String>,
219    pub password: Option<String>,
220}
221
222/// Verification token creation data
223#[derive(Debug, Clone)]
224pub struct CreateVerification {
225    pub identifier: String,
226    pub value: String,
227    pub expires_at: DateTime<Utc>,
228}
229
230impl CreateUser {
231    pub fn new() -> Self {
232        Self {
233            id: Some(Uuid::new_v4().to_string()),
234            email: None,
235            name: None,
236            image: None,
237            email_verified: None,
238            password: None,
239            username: None,
240            display_username: None,
241            role: None,
242            metadata: None,
243        }
244    }
245    
246    pub fn with_email(mut self, email: impl Into<String>) -> Self {
247        self.email = Some(email.into());
248        self
249    }
250    
251    pub fn with_name(mut self, name: impl Into<String>) -> Self {
252        self.name = Some(name.into());
253        self
254    }
255    
256    pub fn with_email_verified(mut self, verified: bool) -> Self {
257        self.email_verified = Some(verified);
258        self
259    }
260    
261    pub fn with_password(mut self, password: impl Into<String>) -> Self {
262        self.password = Some(password.into());
263        self
264    }
265    
266    pub fn with_username(mut self, username: impl Into<String>) -> Self {
267        self.username = Some(username.into());
268        self
269    }
270    
271    pub fn with_role(mut self, role: impl Into<String>) -> Self {
272        self.role = Some(role.into());
273        self
274    }
275    
276    pub fn with_metadata(mut self, metadata: HashMap<String, serde_json::Value>) -> Self {
277        self.metadata = Some(metadata);
278        self
279    }
280}
281
282impl Default for CreateUser {
283    fn default() -> Self {
284        Self::new()
285    }
286}
287
288impl AuthRequest {
289    pub fn new(method: HttpMethod, path: impl Into<String>) -> Self {
290        Self {
291            method,
292            path: path.into(),
293            headers: HashMap::new(),
294            body: None,
295            query: HashMap::new(),
296        }
297    }
298    
299    pub fn method(&self) -> &HttpMethod {
300        &self.method
301    }
302    
303    pub fn path(&self) -> &str {
304        &self.path
305    }
306    
307    pub fn header(&self, name: &str) -> Option<&String> {
308        self.headers.get(name)
309    }
310    
311    pub fn body_as_json<T: for<'de> Deserialize<'de>>(&self) -> Result<T, serde_json::Error> {
312        if let Some(body) = &self.body {
313            serde_json::from_slice(body)
314        } else {
315            serde_json::from_str("{}")
316        }
317    }
318}
319
320impl AuthResponse {
321    pub fn new(status: u16) -> Self {
322        Self {
323            status,
324            headers: HashMap::new(),
325            body: Vec::new(),
326        }
327    }
328    
329    pub fn json<T: Serialize>(status: u16, data: &T) -> Result<Self, serde_json::Error> {
330        let body = serde_json::to_vec(data)?;
331        let mut headers = HashMap::new();
332        headers.insert("content-type".to_string(), "application/json".to_string());
333        
334        Ok(Self {
335            status,
336            headers,
337            body,
338        })
339    }
340    
341    pub fn text(status: u16, text: impl Into<String>) -> Self {
342        let body = text.into().into_bytes();
343        let mut headers = HashMap::new();
344        headers.insert("content-type".to_string(), "text/plain".to_string());
345        
346        Self {
347            status,
348            headers,
349            body,
350        }
351    }
352    
353    pub fn with_header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
354        self.headers.insert(name.into(), value.into());
355        self
356    }
357}
358
359// User profile management request/response structures
360#[derive(Debug, Deserialize)]
361pub struct UpdateUserRequest {
362    pub name: Option<String>,
363    pub email: Option<String>,
364    pub image: Option<String>,
365    pub username: Option<String>,
366    #[serde(rename = "displayUsername")]
367    pub display_username: Option<String>,
368    pub role: Option<String>,
369    pub metadata: Option<HashMap<String, serde_json::Value>>,
370}
371
372#[derive(Debug, Serialize)]
373pub struct UpdateUserResponse {
374    pub user: User,
375}
376
377#[derive(Debug, Serialize)]
378pub struct DeleteUserResponse {
379    pub success: bool,
380    pub message: String,
381}
382
383// Manual FromRow implementations for PostgreSQL
384#[cfg(feature = "sqlx-postgres")]
385mod postgres_impls {
386    use super::*;
387    use sqlx::{FromRow, Row};
388    use sqlx::postgres::PgRow;
389    
390    impl FromRow<'_, PgRow> for User {
391        fn from_row(row: &PgRow) -> Result<Self, sqlx::Error> {
392            Ok(Self {
393                id: row.try_get("id")?,
394                name: row.try_get("name")?,
395                email: row.try_get("email")?,
396                email_verified: row.try_get("email_verified")?,
397                image: row.try_get("image")?,
398                created_at: row.try_get("created_at")?,
399                updated_at: row.try_get("updated_at")?,
400                username: row.try_get("username")?,
401                display_username: row.try_get("display_username")?,
402                two_factor_enabled: row.try_get("two_factor_enabled").unwrap_or(false),
403                role: row.try_get("role")?,
404                banned: row.try_get("banned").unwrap_or(false),
405                ban_reason: row.try_get("ban_reason")?,
406                ban_expires: row.try_get("ban_expires")?,
407                metadata: {
408                    let json_value: sqlx::types::Json<HashMap<String, serde_json::Value>> = 
409                        row.try_get("metadata")?;
410                    json_value.0
411                },
412            })
413        }
414    }
415    
416    impl FromRow<'_, PgRow> for Session {
417        fn from_row(row: &PgRow) -> Result<Self, sqlx::Error> {
418            Ok(Self {
419                id: row.try_get("id")?,
420                expires_at: row.try_get("expires_at")?,
421                token: row.try_get("token")?,
422                created_at: row.try_get("created_at")?,
423                updated_at: row.try_get("updated_at")?,
424                ip_address: row.try_get("ip_address")?,
425                user_agent: row.try_get("user_agent")?,
426                user_id: row.try_get("user_id")?,
427                impersonated_by: row.try_get("impersonated_by")?,
428                active_organization_id: row.try_get("active_organization_id")?,
429                active: row.try_get("active").unwrap_or(true),
430            })
431        }
432    }
433    
434    impl FromRow<'_, PgRow> for Account {
435        fn from_row(row: &PgRow) -> Result<Self, sqlx::Error> {
436            Ok(Self {
437                id: row.try_get("id")?,
438                account_id: row.try_get("account_id")?,
439                provider_id: row.try_get("provider_id")?,
440                user_id: row.try_get("user_id")?,
441                access_token: row.try_get("access_token")?,
442                refresh_token: row.try_get("refresh_token")?,
443                id_token: row.try_get("id_token")?,
444                access_token_expires_at: row.try_get("access_token_expires_at")?,
445                refresh_token_expires_at: row.try_get("refresh_token_expires_at")?,
446                scope: row.try_get("scope")?,
447                password: row.try_get("password")?,
448                created_at: row.try_get("created_at")?,
449                updated_at: row.try_get("updated_at")?,
450            })
451        }
452    }
453}