Skip to main content

better_auth_core/
entity.rs

1//! Entity traits for the Better Auth framework.
2//!
3//! These traits define the interface that entity types must implement.
4//! The framework accesses entity fields through these trait methods,
5//! allowing users to define their own entity structs with custom field names
6//! and extra fields.
7//!
8//! Use `#[derive(AuthUser)]` etc. from `better-auth-derive` to auto-implement
9//! these traits, or implement them manually.
10//!
11//! ## Meta traits
12//!
13//! Each entity trait has a corresponding `Auth*Meta` trait (e.g., `AuthUserMeta`)
14//! that provides table and column name mappings for SQL generation. The derive
15//! macros automatically implement both the entity trait and the meta trait.
16//!
17//! If you implement entity traits **manually** (without derive macros), you must
18//! also implement the corresponding `Auth*Meta` trait. An empty `impl` block is
19//! sufficient to get the default column/table names:
20//!
21//! ```rust,ignore
22//! impl AuthUserMeta for MyUser {}   // uses default column names
23//! impl AuthSessionMeta for MySession {}
24//! ```
25
26use chrono::{DateTime, Utc};
27use serde::Serialize;
28
29use crate::types::InvitationStatus;
30
31/// Metadata key used to store the password hash inside [`AuthUser::metadata()`].
32pub const PASSWORD_HASH_KEY: &str = "password_hash";
33
34/// Trait representing a user entity.
35///
36/// The framework reads user fields through these getters. Custom types
37/// must provide all framework fields and may have additional fields.
38pub trait AuthUser: Clone + Send + Sync + Serialize + std::fmt::Debug + 'static {
39    fn id(&self) -> &str;
40    fn email(&self) -> Option<&str>;
41    fn name(&self) -> Option<&str>;
42    fn email_verified(&self) -> bool;
43    fn image(&self) -> Option<&str>;
44    fn created_at(&self) -> DateTime<Utc>;
45    fn updated_at(&self) -> DateTime<Utc>;
46    fn username(&self) -> Option<&str>;
47    fn display_username(&self) -> Option<&str>;
48    fn two_factor_enabled(&self) -> bool;
49    fn role(&self) -> Option<&str>;
50    fn banned(&self) -> bool;
51    fn ban_reason(&self) -> Option<&str>;
52    fn ban_expires(&self) -> Option<DateTime<Utc>>;
53    fn metadata(&self) -> &serde_json::Value;
54
55    /// Extract the stored password hash from the user metadata.
56    fn password_hash(&self) -> Option<&str> {
57        self.metadata()
58            .get(PASSWORD_HASH_KEY)
59            .and_then(|v| v.as_str())
60    }
61}
62
63/// Trait representing a session entity.
64pub trait AuthSession: Clone + Send + Sync + Serialize + std::fmt::Debug + 'static {
65    fn id(&self) -> &str;
66    fn expires_at(&self) -> DateTime<Utc>;
67    fn token(&self) -> &str;
68    fn created_at(&self) -> DateTime<Utc>;
69    fn updated_at(&self) -> DateTime<Utc>;
70    fn ip_address(&self) -> Option<&str>;
71    fn user_agent(&self) -> Option<&str>;
72    fn user_id(&self) -> &str;
73    fn impersonated_by(&self) -> Option<&str>;
74    fn active_organization_id(&self) -> Option<&str>;
75    fn active(&self) -> bool;
76}
77
78/// Trait representing an account entity (OAuth provider linking).
79pub trait AuthAccount: Clone + Send + Sync + Serialize + std::fmt::Debug + 'static {
80    fn id(&self) -> &str;
81    fn account_id(&self) -> &str;
82    fn provider_id(&self) -> &str;
83    fn user_id(&self) -> &str;
84    fn access_token(&self) -> Option<&str>;
85    fn refresh_token(&self) -> Option<&str>;
86    fn id_token(&self) -> Option<&str>;
87    fn access_token_expires_at(&self) -> Option<DateTime<Utc>>;
88    fn refresh_token_expires_at(&self) -> Option<DateTime<Utc>>;
89    fn scope(&self) -> Option<&str>;
90    fn password(&self) -> Option<&str>;
91    fn created_at(&self) -> DateTime<Utc>;
92    fn updated_at(&self) -> DateTime<Utc>;
93}
94
95/// Trait representing an organization entity.
96pub trait AuthOrganization: Clone + Send + Sync + Serialize + std::fmt::Debug + 'static {
97    fn id(&self) -> &str;
98    fn name(&self) -> &str;
99    fn slug(&self) -> &str;
100    fn logo(&self) -> Option<&str>;
101    fn metadata(&self) -> Option<&serde_json::Value>;
102    fn created_at(&self) -> DateTime<Utc>;
103    fn updated_at(&self) -> DateTime<Utc>;
104}
105
106/// Trait representing an organization member entity.
107pub trait AuthMember: Clone + Send + Sync + Serialize + std::fmt::Debug + 'static {
108    fn id(&self) -> &str;
109    fn organization_id(&self) -> &str;
110    fn user_id(&self) -> &str;
111    fn role(&self) -> &str;
112    fn created_at(&self) -> DateTime<Utc>;
113}
114
115/// Trait representing an invitation entity.
116pub trait AuthInvitation: Clone + Send + Sync + Serialize + std::fmt::Debug + 'static {
117    fn id(&self) -> &str;
118    fn organization_id(&self) -> &str;
119    fn email(&self) -> &str;
120    fn role(&self) -> &str;
121    fn status(&self) -> &InvitationStatus;
122    fn inviter_id(&self) -> &str;
123    fn expires_at(&self) -> DateTime<Utc>;
124    fn created_at(&self) -> DateTime<Utc>;
125
126    /// Check if the invitation is still pending.
127    fn is_pending(&self) -> bool {
128        *self.status() == InvitationStatus::Pending
129    }
130
131    /// Check if the invitation has expired.
132    fn is_expired(&self) -> bool {
133        self.expires_at() < Utc::now()
134    }
135}
136
137/// Trait representing a verification token entity.
138pub trait AuthVerification: Clone + Send + Sync + Serialize + std::fmt::Debug + 'static {
139    fn id(&self) -> &str;
140    fn identifier(&self) -> &str;
141    fn value(&self) -> &str;
142    fn expires_at(&self) -> DateTime<Utc>;
143    fn created_at(&self) -> DateTime<Utc>;
144    fn updated_at(&self) -> DateTime<Utc>;
145}
146
147/// Trait representing a two-factor authentication entity.
148pub trait AuthTwoFactor: Clone + Send + Sync + Serialize + std::fmt::Debug + 'static {
149    fn id(&self) -> &str;
150    fn secret(&self) -> &str;
151    fn backup_codes(&self) -> Option<&str>;
152    fn user_id(&self) -> &str;
153    fn created_at(&self) -> DateTime<Utc>;
154    fn updated_at(&self) -> DateTime<Utc>;
155}
156
157/// Trait representing an API key entity.
158pub trait AuthApiKey: Clone + Send + Sync + Serialize + std::fmt::Debug + 'static {
159    fn id(&self) -> &str;
160    fn name(&self) -> Option<&str>;
161    fn start(&self) -> Option<&str>;
162    fn prefix(&self) -> Option<&str>;
163    fn key_hash(&self) -> &str;
164    fn user_id(&self) -> &str;
165    fn refill_interval(&self) -> Option<i64>;
166    fn refill_amount(&self) -> Option<i64>;
167    fn last_refill_at(&self) -> Option<&str>;
168    fn enabled(&self) -> bool;
169    fn rate_limit_enabled(&self) -> bool;
170    fn rate_limit_time_window(&self) -> Option<i64>;
171    fn rate_limit_max(&self) -> Option<i64>;
172    fn request_count(&self) -> Option<i64>;
173    fn remaining(&self) -> Option<i64>;
174    fn last_request(&self) -> Option<&str>;
175    fn expires_at(&self) -> Option<&str>;
176    fn created_at(&self) -> &str;
177    fn updated_at(&self) -> &str;
178    fn permissions(&self) -> Option<&str>;
179    fn metadata(&self) -> Option<&str>;
180}
181
182/// Trait representing a passkey entity.
183pub trait AuthPasskey: Clone + Send + Sync + Serialize + std::fmt::Debug + 'static {
184    fn id(&self) -> &str;
185    fn name(&self) -> &str;
186    fn public_key(&self) -> &str;
187    fn user_id(&self) -> &str;
188    fn credential_id(&self) -> &str;
189    fn counter(&self) -> u64;
190    fn device_type(&self) -> &str;
191    fn backed_up(&self) -> bool;
192    fn transports(&self) -> Option<&str>;
193    fn created_at(&self) -> DateTime<Utc>;
194}
195
196// ── Column / table meta traits ─────────────────────────────────────────────
197//
198// These traits provide SQL column and table name mappings for each entity.
199// The default implementations return the standard names used by the built-in
200// types. When a user defines custom entity structs with different field names,
201// the `#[derive(AuthUser)]` (etc.) macros generate overrides so that
202// SqlxAdapter writes to the correct columns.
203//
204// Users can also override the table name with `#[auth(table = "my_users")]`
205// at the struct level.
206
207/// SQL column/table metadata for [`AuthUser`] entities.
208///
209/// These `Auth*Meta` traits are used by the `SqlxAdapter` to build SQL
210/// statements (INSERT/UPDATE/DELETE/SELECT) using the *correct* table and column
211/// names for your entity types.
212///
213/// # Customizing mappings
214///
215/// When using the derive macros (`#[derive(AuthUser)]`, etc.), you can customize
216/// the names used by the generated `Auth*Meta` impls:
217///
218/// - Struct-level: override the table name with `#[auth(table = "...")]`
219/// - Field-level: override the SQL column name with `#[auth(column = "...")]`
220///
221/// # `FromRow` consistency
222///
223/// If you implement `sqlx::FromRow` manually, ensure the column names you read
224/// match the names returned by the corresponding `Auth*Meta` trait. Alternatively,
225/// you can use `#[auth(from_row)]` to generate a `FromRow` implementation that
226/// stays in sync with the `Auth*Meta` mappings.
227pub trait AuthUserMeta {
228    fn table() -> &'static str {
229        "users"
230    }
231    fn col_id() -> &'static str {
232        "id"
233    }
234    fn col_email() -> &'static str {
235        "email"
236    }
237    fn col_name() -> &'static str {
238        "name"
239    }
240    fn col_image() -> &'static str {
241        "image"
242    }
243    fn col_email_verified() -> &'static str {
244        "email_verified"
245    }
246    fn col_created_at() -> &'static str {
247        "created_at"
248    }
249    fn col_updated_at() -> &'static str {
250        "updated_at"
251    }
252    fn col_metadata() -> &'static str {
253        "metadata"
254    }
255    fn col_username() -> &'static str {
256        "username"
257    }
258    fn col_display_username() -> &'static str {
259        "display_username"
260    }
261    fn col_two_factor_enabled() -> &'static str {
262        "two_factor_enabled"
263    }
264    fn col_role() -> &'static str {
265        "role"
266    }
267    fn col_banned() -> &'static str {
268        "banned"
269    }
270    fn col_ban_reason() -> &'static str {
271        "ban_reason"
272    }
273    fn col_ban_expires() -> &'static str {
274        "ban_expires"
275    }
276}
277
278/// SQL column/table metadata for [`AuthSession`] entities.
279pub trait AuthSessionMeta {
280    fn table() -> &'static str {
281        "sessions"
282    }
283    fn col_id() -> &'static str {
284        "id"
285    }
286    fn col_expires_at() -> &'static str {
287        "expires_at"
288    }
289    fn col_token() -> &'static str {
290        "token"
291    }
292    fn col_created_at() -> &'static str {
293        "created_at"
294    }
295    fn col_updated_at() -> &'static str {
296        "updated_at"
297    }
298    fn col_ip_address() -> &'static str {
299        "ip_address"
300    }
301    fn col_user_agent() -> &'static str {
302        "user_agent"
303    }
304    fn col_user_id() -> &'static str {
305        "user_id"
306    }
307    fn col_impersonated_by() -> &'static str {
308        "impersonated_by"
309    }
310    fn col_active_organization_id() -> &'static str {
311        "active_organization_id"
312    }
313    fn col_active() -> &'static str {
314        "active"
315    }
316}
317
318/// SQL column/table metadata for [`AuthAccount`] entities.
319pub trait AuthAccountMeta {
320    fn table() -> &'static str {
321        "accounts"
322    }
323    fn col_id() -> &'static str {
324        "id"
325    }
326    fn col_account_id() -> &'static str {
327        "account_id"
328    }
329    fn col_provider_id() -> &'static str {
330        "provider_id"
331    }
332    fn col_user_id() -> &'static str {
333        "user_id"
334    }
335    fn col_access_token() -> &'static str {
336        "access_token"
337    }
338    fn col_refresh_token() -> &'static str {
339        "refresh_token"
340    }
341    fn col_id_token() -> &'static str {
342        "id_token"
343    }
344    fn col_access_token_expires_at() -> &'static str {
345        "access_token_expires_at"
346    }
347    fn col_refresh_token_expires_at() -> &'static str {
348        "refresh_token_expires_at"
349    }
350    fn col_scope() -> &'static str {
351        "scope"
352    }
353    fn col_password() -> &'static str {
354        "password"
355    }
356    fn col_created_at() -> &'static str {
357        "created_at"
358    }
359    fn col_updated_at() -> &'static str {
360        "updated_at"
361    }
362}
363
364/// SQL column/table metadata for [`AuthOrganization`] entities.
365pub trait AuthOrganizationMeta {
366    fn table() -> &'static str {
367        "organization"
368    }
369    fn col_id() -> &'static str {
370        "id"
371    }
372    fn col_name() -> &'static str {
373        "name"
374    }
375    fn col_slug() -> &'static str {
376        "slug"
377    }
378    fn col_logo() -> &'static str {
379        "logo"
380    }
381    fn col_metadata() -> &'static str {
382        "metadata"
383    }
384    fn col_created_at() -> &'static str {
385        "created_at"
386    }
387    fn col_updated_at() -> &'static str {
388        "updated_at"
389    }
390}
391
392/// SQL column/table metadata for [`AuthMember`] entities.
393pub trait AuthMemberMeta {
394    fn table() -> &'static str {
395        "member"
396    }
397    fn col_id() -> &'static str {
398        "id"
399    }
400    fn col_organization_id() -> &'static str {
401        "organization_id"
402    }
403    fn col_user_id() -> &'static str {
404        "user_id"
405    }
406    fn col_role() -> &'static str {
407        "role"
408    }
409    fn col_created_at() -> &'static str {
410        "created_at"
411    }
412}
413
414/// SQL column/table metadata for [`AuthInvitation`] entities.
415pub trait AuthInvitationMeta {
416    fn table() -> &'static str {
417        "invitation"
418    }
419    fn col_id() -> &'static str {
420        "id"
421    }
422    fn col_organization_id() -> &'static str {
423        "organization_id"
424    }
425    fn col_email() -> &'static str {
426        "email"
427    }
428    fn col_role() -> &'static str {
429        "role"
430    }
431    fn col_status() -> &'static str {
432        "status"
433    }
434    fn col_inviter_id() -> &'static str {
435        "inviter_id"
436    }
437    fn col_expires_at() -> &'static str {
438        "expires_at"
439    }
440    fn col_created_at() -> &'static str {
441        "created_at"
442    }
443}
444
445/// SQL column/table metadata for [`AuthVerification`] entities.
446pub trait AuthVerificationMeta {
447    fn table() -> &'static str {
448        "verifications"
449    }
450    fn col_id() -> &'static str {
451        "id"
452    }
453    fn col_identifier() -> &'static str {
454        "identifier"
455    }
456    fn col_value() -> &'static str {
457        "value"
458    }
459    fn col_expires_at() -> &'static str {
460        "expires_at"
461    }
462    fn col_created_at() -> &'static str {
463        "created_at"
464    }
465    fn col_updated_at() -> &'static str {
466        "updated_at"
467    }
468}
469
470/// SQL column/table metadata for [`AuthTwoFactor`] entities.
471pub trait AuthTwoFactorMeta {
472    fn table() -> &'static str {
473        "two_factor"
474    }
475    fn col_id() -> &'static str {
476        "id"
477    }
478    fn col_secret() -> &'static str {
479        "secret"
480    }
481    fn col_backup_codes() -> &'static str {
482        "backup_codes"
483    }
484    fn col_user_id() -> &'static str {
485        "user_id"
486    }
487    fn col_created_at() -> &'static str {
488        "created_at"
489    }
490    fn col_updated_at() -> &'static str {
491        "updated_at"
492    }
493}
494
495/// SQL column/table metadata for [`AuthApiKey`] entities.
496pub trait AuthApiKeyMeta {
497    fn table() -> &'static str {
498        "api_keys"
499    }
500    fn col_id() -> &'static str {
501        "id"
502    }
503    fn col_name() -> &'static str {
504        "name"
505    }
506    fn col_start() -> &'static str {
507        "start"
508    }
509    fn col_prefix() -> &'static str {
510        "prefix"
511    }
512    /// Database column for the API key hash.
513    ///
514    /// The default better-auth schema uses the column name `key` (even though it
515    /// may be a reserved word in some SQL dialects). The `SqlxAdapter` quotes SQL
516    /// identifiers, so using `key` here is safe.
517    fn col_key_hash() -> &'static str {
518        "key"
519    }
520    fn col_user_id() -> &'static str {
521        "user_id"
522    }
523    fn col_refill_interval() -> &'static str {
524        "refill_interval"
525    }
526    fn col_refill_amount() -> &'static str {
527        "refill_amount"
528    }
529    fn col_last_refill_at() -> &'static str {
530        "last_refill_at"
531    }
532    fn col_enabled() -> &'static str {
533        "enabled"
534    }
535    fn col_rate_limit_enabled() -> &'static str {
536        "rate_limit_enabled"
537    }
538    fn col_rate_limit_time_window() -> &'static str {
539        "rate_limit_time_window"
540    }
541    fn col_rate_limit_max() -> &'static str {
542        "rate_limit_max"
543    }
544    fn col_request_count() -> &'static str {
545        "request_count"
546    }
547    fn col_remaining() -> &'static str {
548        "remaining"
549    }
550    fn col_last_request() -> &'static str {
551        "last_request"
552    }
553    fn col_expires_at() -> &'static str {
554        "expires_at"
555    }
556    fn col_created_at() -> &'static str {
557        "created_at"
558    }
559    fn col_updated_at() -> &'static str {
560        "updated_at"
561    }
562    fn col_permissions() -> &'static str {
563        "permissions"
564    }
565    fn col_metadata() -> &'static str {
566        "metadata"
567    }
568}
569
570/// SQL column/table metadata for [`AuthPasskey`] entities.
571pub trait AuthPasskeyMeta {
572    fn table() -> &'static str {
573        "passkeys"
574    }
575    fn col_id() -> &'static str {
576        "id"
577    }
578    fn col_name() -> &'static str {
579        "name"
580    }
581    fn col_public_key() -> &'static str {
582        "public_key"
583    }
584    fn col_user_id() -> &'static str {
585        "user_id"
586    }
587    fn col_credential_id() -> &'static str {
588        "credential_id"
589    }
590    fn col_counter() -> &'static str {
591        "counter"
592    }
593    fn col_device_type() -> &'static str {
594        "device_type"
595    }
596    fn col_backed_up() -> &'static str {
597        "backed_up"
598    }
599    fn col_transports() -> &'static str {
600        "transports"
601    }
602    fn col_created_at() -> &'static str {
603        "created_at"
604    }
605}
606
607/// Minimal user info for member-related API responses.
608///
609/// This is a concrete framework type (not generic) used to project
610/// user fields into member responses.
611#[derive(Debug, Clone, Serialize, Deserialize)]
612pub struct MemberUserView {
613    pub id: String,
614    pub email: Option<String>,
615    pub name: Option<String>,
616    pub image: Option<String>,
617}
618
619impl MemberUserView {
620    /// Construct from any type implementing [`AuthUser`].
621    pub fn from_user(user: &impl AuthUser) -> Self {
622        Self {
623            id: user.id().to_string(),
624            email: user.email().map(|s| s.to_string()),
625            name: user.name().map(|s| s.to_string()),
626            image: user.image().map(|s| s.to_string()),
627        }
628    }
629}
630
631use serde::Deserialize;