Skip to content

SeaORM

SeaOrmStore implements the same AuthStore contract as the SQLx store, with full feature parity: every plugin that works on SQLx works on SeaORM. Choose it when your application already uses SeaORM entities, migrations and connections.

Enable the seaorm feature. SeaORM is re-exported as alibi::seaorm::sea_orm, so you do not need a separate sea-orm dependency for the generated code:

Cargo.toml
alibi = { version = "0.1.1", default-features = false, features = ["axum", "seaorm", "rustls"] }
axum = "0.8"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"

If your application also depends on sea-orm directly, use the same 2.x version to share types. TLS comes from rustls or native-tls (Cargo features).

Terminal window
alibi generate --backend seaorm -o src/auth_schema.rs
alibi generate --backend seaorm --plugins organization,two-factor -o src/auth_schema.rs

The file defines the four entities, an AppAuthSchema, and run_app_migrations(&DatabaseConnection):

src/auth_schema.rs (excerpt)
mod user {
use super::*;
#[derive(Clone, Debug, serde::Serialize, DeriveEntityModel, AuthEntity)]
#[auth(role = "user")]
#[sea_orm(table_name = "users")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
pub name: Option<String>,
pub email: Option<String>,
pub email_verified: bool,
pub image: Option<String>,
pub created_at: DateTimeUtc,
pub updated_at: DateTimeUtc,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}
}
use crate::auth_schema::{AppAuthSchema, run_app_migrations};
use alibi::plugins::EmailPasswordPlugin;
use alibi::seaorm::{Database, SeaOrmStore};
use alibi::{AuthConfig, BetterAuth};
async fn build_auth(
secret: &str,
database_url: &str,
) -> Result<BetterAuth<AppAuthSchema>, Box<dyn std::error::Error>> {
let config = AuthConfig::new(secret).base_url("http://localhost:3000");
let database = Database::connect(database_url).await?;
run_app_migrations(&database).await?;
let store = SeaOrmStore::<AppAuthSchema>::new(config.clone(), database);
Ok(BetterAuth::<AppAuthSchema>::new(config)
.store(store)
.plugin(EmailPasswordPlugin::new().enable_signup(true))
.build()
.await?)
}

Database::connect takes a SQLite (sqlite:) or PostgreSQL (postgres:) URL. To tune the pool, build ConnectOptions and call Database::connect(options) — SeaORM’s DatabaseConnection is a cheap handle that you can share between your application and authentication.

Derive DeriveEntityModel and alibi::seaorm::AuthEntity on your model, then select it in the schema:

#[derive(Clone, Debug, serde::Serialize, DeriveEntityModel, alibi::seaorm::AuthEntity)]
#[auth(role = "user", id_generator = "crate::ids::new_user_id")]
#[sea_orm(table_name = "app_users")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
pub email: Option<String>,
pub name: Option<String>,
pub email_verified: bool,
pub image: Option<String>,
pub created_at: DateTimeUtc,
pub updated_at: DateTimeUtc,
pub locale: Option<String>,
}

The container attributes match SQLx: role, id_generator and secondary_storage; the table name comes from sea_orm(table_name). Required fields per role are checked at compile time.

For PostgreSQL fixed-width CHAR(n) ids, use #[sea_orm(column_type = "Char(Some(30))", save_as = "bpchar")]; omit save_as on SQLite. See Existing databases.

The hook trait is shared with SQLx; use SeaOrmBackend:

use crate::auth_schema::AppAuthSchema;
use async_trait::async_trait;
use alibi::prelude::CreateUser;
use alibi::seaorm::{SeaOrmBackend, SeaOrmStore};
use alibi::store::{DatabaseHookContext, DatabaseHooks, HookControl};
use alibi::AuthResult;
struct DefaultName;
#[async_trait]
impl DatabaseHooks<AppAuthSchema, SeaOrmBackend> for DefaultName {
async fn before_create_user(
&self,
user: &mut CreateUser,
_: &DatabaseHookContext<'_, SeaOrmBackend>,
) -> AuthResult<HookControl> {
user.name.get_or_insert_with(|| "New user".into());
Ok(HookControl::Continue)
}
}
fn hooked(store: SeaOrmStore<AppAuthSchema>) -> SeaOrmStore<AppAuthSchema> {
store.hook(DefaultName)
}

See Hooks.

run_app_migrations creates the generated entities with Schema::create_table_from_entity — convenient for new databases and tests. In production, keep migrations in your sea-orm-migration crate. The store can also install the library’s bundled schema (all plugin tables with keys and indexes) through SchemaMigrator::migrate; see Database. Rate-limit storage in the database has its own entity and migrator: alibi::seaorm::SeaOrmRateLimitStorage (Rate limiting).

See the official database guide.