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