Skip to main content

better_auth_schema_registry/
lib.rs

1//! Authoritative field registry for Alibi entity schemas.
2//!
3//! Shared by the `AuthEntity` proc macro (for compile-time validation)
4//! and the CLI (for code generation). This is the single source of truth
5//! for which fields belong to core vs which are plugin-provided.
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8pub enum EntityRole {
9    User,
10    Session,
11    Account,
12    Verification,
13}
14
15#[derive(Clone, Copy, Debug)]
16pub struct FieldDef {
17    pub name: &'static str,
18    /// Rust type as it appears in a `SeaORM` `Model` struct,
19    /// e.g. `"Option<String>"`, `"bool"`, `"DateTimeUtc"`, `"Json"`.
20    pub ty: &'static str,
21    pub is_primary_key: bool,
22    pub column_name: Option<&'static str>,
23    /// Database string default, rendered as a literal by each backend.
24    pub default_value: Option<&'static str>,
25}
26
27#[derive(Clone, Copy, Debug)]
28pub struct PluginSchema {
29    pub name: &'static str,
30    pub user_fields: &'static [FieldDef],
31    pub session_fields: &'static [FieldDef],
32    pub extra_entities: &'static [ExtraEntitySchema],
33}
34
35#[derive(Clone, Copy, Debug)]
36pub struct ExtraEntitySchema {
37    pub mod_name: &'static str,
38    pub table_name: &'static str,
39    pub role: Option<EntityRole>,
40    pub fields: &'static [FieldDef],
41}
42
43macro_rules! f {
44    ($name:expr, $ty:expr, $default:expr) => {
45        FieldDef {
46            default_value: Some($default),
47            ..f!($name, $ty)
48        }
49    };
50    ($name:expr, $ty:expr) => {
51        FieldDef {
52            name: $name,
53            ty: $ty,
54            is_primary_key: false,
55            column_name: None,
56            default_value: None,
57        }
58    };
59}
60
61macro_rules! pk {
62    ($name:expr, $ty:expr) => {
63        FieldDef {
64            name: $name,
65            ty: $ty,
66            is_primary_key: true,
67            column_name: None,
68            default_value: None,
69        }
70    };
71}
72
73macro_rules! f_col {
74    ($name:expr, $ty:expr, $column_name:expr) => {
75        FieldDef {
76            name: $name,
77            ty: $ty,
78            is_primary_key: false,
79            column_name: Some($column_name),
80            default_value: None,
81        }
82    };
83}
84
85// ── Core fields ──────────────────────────────────────────────────────
86
87static USER_CORE: &[FieldDef] = &[
88    pk!("id", "String"),
89    f!("name", "Option<String>"),
90    f!("email", "Option<String>"),
91    f!("email_verified", "bool"),
92    f!("image", "Option<String>"),
93    f!("created_at", "DateTimeUtc"),
94    f!("updated_at", "DateTimeUtc"),
95];
96
97static SESSION_CORE: &[FieldDef] = &[
98    pk!("id", "String"),
99    f!("expires_at", "DateTimeUtc"),
100    f!("token", "String"),
101    f!("created_at", "DateTimeUtc"),
102    f!("updated_at", "DateTimeUtc"),
103    f!("ip_address", "Option<String>"),
104    f!("user_agent", "Option<String>"),
105    f!("user_id", "String"),
106    f!("active", "bool"),
107];
108
109static ACCOUNT_CORE: &[FieldDef] = &[
110    pk!("id", "String"),
111    f!("account_id", "String"),
112    f!("provider_id", "String"),
113    f!("user_id", "String"),
114    f!("access_token", "Option<String>"),
115    f!("refresh_token", "Option<String>"),
116    f!("id_token", "Option<String>"),
117    f!("access_token_expires_at", "Option<DateTimeUtc>"),
118    f!("refresh_token_expires_at", "Option<DateTimeUtc>"),
119    f!("scope", "Option<String>"),
120    f!("password", "Option<String>"),
121    f!("created_at", "DateTimeUtc"),
122    f!("updated_at", "DateTimeUtc"),
123];
124
125static VERIFICATION_CORE: &[FieldDef] = &[
126    pk!("id", "String"),
127    f!("identifier", "String"),
128    f!("value", "String"),
129    f!("expires_at", "DateTimeUtc"),
130    f!("created_at", "DateTimeUtc"),
131    f!("updated_at", "DateTimeUtc"),
132];
133
134// ── Plugin schemas ───────────────────────────────────────────────────
135
136static PLUGINS: &[PluginSchema] = &[
137    PluginSchema {
138        name: "siwe",
139        user_fields: &[],
140        session_fields: &[],
141        extra_entities: &[ExtraEntitySchema {
142            mod_name: "wallet_address",
143            table_name: "wallet_address",
144            role: None,
145            fields: &[
146                pk!("id", "String"),
147                f!("user_id", "String"),
148                f!("address", "String"),
149                f!("chain_id", "f64"),
150                f!("is_primary", "bool"),
151                f!("created_at", "DateTimeUtc"),
152            ],
153        }],
154    },
155    PluginSchema {
156        name: "jwt",
157        user_fields: &[],
158        session_fields: &[],
159        extra_entities: &[ExtraEntitySchema {
160            mod_name: "jwk",
161            table_name: "jwks",
162            role: None,
163            fields: &[
164                pk!("id", "String"),
165                f!("public_key", "String"),
166                f!("private_key", "String"),
167                f!("created_at", "DateTimeUtc"),
168                f!("expires_at", "Option<DateTimeUtc>"),
169                f!("alg", "Option<String>"),
170                f!("crv", "Option<String>"),
171            ],
172        }],
173    },
174    PluginSchema {
175        name: "anonymous",
176        user_fields: &[f!("is_anonymous", "Option<bool>")],
177        session_fields: &[],
178        extra_entities: &[],
179    },
180    PluginSchema {
181        name: "phone-number",
182        user_fields: &[
183            f!("phone_number", "Option<String>"),
184            f!("phone_number_verified", "Option<bool>"),
185        ],
186        session_fields: &[],
187        extra_entities: &[],
188    },
189    PluginSchema {
190        name: "last-login-method",
191        user_fields: &[f!("last_login_method", "Option<String>")],
192        session_fields: &[],
193        extra_entities: &[],
194    },
195    PluginSchema {
196        name: "organization-teams",
197        user_fields: &[],
198        session_fields: &[f!("active_team_id", "Option<String>")],
199        extra_entities: &[
200            ExtraEntitySchema {
201                mod_name: "team",
202                table_name: "team",
203                role: None,
204                fields: &[
205                    pk!("id", "String"),
206                    f!("name", "String"),
207                    f!("organization_id", "String"),
208                    f!("member_count", "i64"),
209                    f!("created_at", "DateTimeUtc"),
210                    f!("updated_at", "Option<DateTimeUtc>"),
211                ],
212            },
213            ExtraEntitySchema {
214                mod_name: "team_member",
215                table_name: "team_member",
216                role: None,
217                fields: &[
218                    pk!("id", "String"),
219                    f!("team_id", "String"),
220                    f!("user_id", "String"),
221                    f!("membership_key", "Option<String>"),
222                    f!("created_at", "DateTimeUtc"),
223                ],
224            },
225        ],
226    },
227    PluginSchema {
228        name: "organization-dynamic-roles",
229        user_fields: &[],
230        session_fields: &[],
231        extra_entities: &[ExtraEntitySchema {
232            mod_name: "organization_role",
233            table_name: "organization_role",
234            role: None,
235            fields: &[
236                pk!("id", "String"),
237                f!("organization_id", "String"),
238                f!("role", "String"),
239                f!("permission", "String"),
240                f!("created_at", "DateTimeUtc"),
241                f!("updated_at", "Option<DateTimeUtc>"),
242            ],
243        }],
244    },
245    PluginSchema {
246        name: "username",
247        user_fields: &[
248            f!("username", "Option<String>"),
249            f!("display_username", "Option<String>"),
250        ],
251        session_fields: &[],
252        extra_entities: &[],
253    },
254    PluginSchema {
255        name: "two-factor",
256        user_fields: &[f!("two_factor_enabled", "Option<bool>")],
257        session_fields: &[],
258        extra_entities: &[ExtraEntitySchema {
259            mod_name: "two_factor",
260            table_name: "two_factor",
261            role: None,
262            fields: &[
263                pk!("id", "String"),
264                f!("secret", "String"),
265                f!("backup_codes", "String"),
266                f!("user_id", "String"),
267                f!("verified", "Option<bool>"),
268                f!("failed_verification_count", "Option<f64>"),
269                f!("locked_until", "Option<DateTimeUtc>"),
270                f!("created_at", "DateTimeUtc"),
271                f!("updated_at", "DateTimeUtc"),
272            ],
273        }],
274    },
275    PluginSchema {
276        name: "device-authorization",
277        user_fields: &[],
278        session_fields: &[],
279        extra_entities: &[ExtraEntitySchema {
280            mod_name: "device_code",
281            table_name: "device_code",
282            role: None,
283            fields: &[
284                pk!("id", "String"),
285                f!("device_code", "String"),
286                f!("user_code", "String"),
287                f!("user_id", "Option<String>"),
288                f!("expires_at", "DateTimeUtc"),
289                f!("status", "String"),
290                f!("last_polled_at", "Option<DateTimeUtc>"),
291                f!("polling_interval", "Option<i64>"),
292                f!("client_id", "Option<String>"),
293                f!("scope", "Option<String>"),
294            ],
295        }],
296    },
297    PluginSchema {
298        name: "admin",
299        user_fields: &[
300            f!("role", "Option<String>"),
301            f!("banned", "Option<bool>"),
302            f!("ban_reason", "Option<String>"),
303            f!("ban_expires", "Option<DateTimeUtc>"),
304            f!("metadata", "Json"),
305        ],
306        session_fields: &[f!("impersonated_by", "Option<String>")],
307        extra_entities: &[],
308    },
309    PluginSchema {
310        name: "organization",
311        user_fields: &[],
312        session_fields: &[f!("active_organization_id", "Option<String>")],
313        extra_entities: &[
314            ExtraEntitySchema {
315                mod_name: "organization",
316                table_name: "organization",
317                role: None,
318                fields: &[
319                    pk!("id", "String"),
320                    f!("name", "String"),
321                    f!("slug", "String"),
322                    f!("logo", "Option<String>"),
323                    f!("metadata", "Option<Json>"),
324                    f!("created_at", "DateTimeUtc"),
325                    f!("updated_at", "DateTimeUtc"),
326                ],
327            },
328            ExtraEntitySchema {
329                mod_name: "member",
330                table_name: "member",
331                role: None,
332                fields: &[
333                    pk!("id", "String"),
334                    f!("organization_id", "String"),
335                    f!("user_id", "String"),
336                    f!("role", "String"),
337                    f!("created_at", "DateTimeUtc"),
338                ],
339            },
340            ExtraEntitySchema {
341                mod_name: "invitation",
342                table_name: "invitation",
343                role: None,
344                fields: &[
345                    pk!("id", "String"),
346                    f!("organization_id", "String"),
347                    f!("email", "String"),
348                    f!("role", "String"),
349                    f!("team_id", "Option<String>"),
350                    f!("status", "String"),
351                    f!("inviter_id", "String"),
352                    f!("expires_at", "DateTimeUtc"),
353                    f!("created_at", "DateTimeUtc"),
354                ],
355            },
356        ],
357    },
358    PluginSchema {
359        name: "api-key",
360        user_fields: &[],
361        session_fields: &[],
362        extra_entities: &[ExtraEntitySchema {
363            mod_name: "api_key",
364            table_name: "api_keys",
365            role: None,
366            fields: &[
367                pk!("id", "String"),
368                f!("name", "Option<String>"),
369                f!("start", "Option<String>"),
370                f!("prefix", "Option<String>"),
371                f_col!("key_hash", "String", "key"),
372                f!("reference_id", "String"),
373                f!("config_id", "String", "default"),
374                f!("refill_interval", "Option<f64>"),
375                f!("refill_amount", "Option<f64>"),
376                f!("last_refill_at", "Option<DateTimeUtc>"),
377                f!("enabled", "bool"),
378                f!("rate_limit_enabled", "bool"),
379                f!("rate_limit_time_window", "Option<f64>"),
380                f!("rate_limit_max", "Option<f64>"),
381                f!("request_count", "Option<f64>"),
382                f!("remaining", "Option<f64>"),
383                f!("last_request", "Option<DateTimeUtc>"),
384                f!("expires_at", "Option<DateTimeUtc>"),
385                f!("created_at", "DateTimeUtc"),
386                f!("updated_at", "DateTimeUtc"),
387                f!("permissions", "Option<String>"),
388                f!("metadata", "Option<String>"),
389            ],
390        }],
391    },
392    PluginSchema {
393        name: "passkey",
394        user_fields: &[],
395        session_fields: &[],
396        extra_entities: &[ExtraEntitySchema {
397            mod_name: "passkey",
398            table_name: "passkeys",
399            role: None,
400            fields: &[
401                pk!("id", "String"),
402                f!("name", "Option<String>"),
403                f!("public_key", "String"),
404                f!("user_id", "String"),
405                f!("credential_id", "String"),
406                f!("counter", "i64"),
407                f!("device_type", "String"),
408                f!("backed_up", "bool"),
409                f!("transports", "Option<String>"),
410                f!("credential", "String"),
411                f!("aaguid", "Option<String>"),
412                f!("created_at", "DateTimeUtc"),
413                f!("updated_at", "DateTimeUtc"),
414            ],
415        }],
416    },
417];
418
419/// Core fields that are always required for a given entity role.
420#[must_use]
421pub fn core_fields(role: EntityRole) -> &'static [FieldDef] {
422    match role {
423        EntityRole::User => USER_CORE,
424        EntityRole::Session => SESSION_CORE,
425        EntityRole::Account => ACCOUNT_CORE,
426        EntityRole::Verification => VERIFICATION_CORE,
427    }
428}
429
430/// Plugin schemas — each plugin can add fields to user and/or session entities.
431#[must_use]
432pub fn plugin_schemas() -> &'static [PluginSchema] {
433    PLUGINS
434}
435
436/// All plugin field names for a given role (convenience for the macro).
437#[must_use]
438pub fn plugin_field_names(role: EntityRole) -> Vec<&'static str> {
439    plugin_schemas()
440        .iter()
441        .flat_map(|p| match role {
442            EntityRole::User => p.user_fields.iter(),
443            EntityRole::Session => p.session_fields.iter(),
444            EntityRole::Account | EntityRole::Verification => [].iter(),
445        })
446        .map(|f| f.name)
447        .collect()
448}
449
450/// Core field names only (convenience for the macro).
451#[must_use]
452pub fn core_field_names(role: EntityRole) -> Vec<&'static str> {
453    core_fields(role).iter().map(|f| f.name).collect()
454}