torii_storage_seaorm/lib.rs
1//! SeaORM storage backend for Torii
2//!
3//! This crate provides a SeaORM-based storage implementation for the Torii authentication framework.
4//! SeaORM is a modern async ORM for Rust that provides type-safe database operations and supports
5//! multiple database backends including PostgreSQL, MySQL, and SQLite.
6//!
7//! # Features
8//!
9//! - **Multi-Database Support**: Works with PostgreSQL, MySQL, and SQLite through SeaORM
10//! - **Type-Safe Operations**: Leverages SeaORM's compile-time query validation
11//! - **Async/Await**: Fully async database operations with tokio
12//! - **Automatic Migrations**: Built-in schema migration management
13//! - **User Management**: Store and retrieve user accounts with email verification support
14//! - **Session Management**: Handle user sessions with configurable expiration
15//! - **Password Authentication**: Secure password hashing and verification
16//! - **OAuth Integration**: Store OAuth account connections and tokens
17//! - **Passkey Support**: WebAuthn/FIDO2 passkey storage and challenge management
18//!
19//! # Usage
20//!
21//! ```rust,no_run
22//! use torii_storage_seaorm::SeaORMStorage;
23//! use torii_core::UserId;
24//!
25//! #[tokio::main]
26//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
27//! // Connect to database (supports PostgreSQL, MySQL, SQLite)
28//! let storage = SeaORMStorage::connect("sqlite://todos.db?mode=rwc").await?;
29//!
30//! // Run migrations to set up the schema
31//! storage.migrate().await?;
32//!
33//! // Convert to repository provider and use with Torii
34//! let repositories = std::sync::Arc::new(storage.into_repository_provider());
35//! let torii = torii::Torii::new(repositories);
36//!
37//! Ok(())
38//! }
39//! ```
40//!
41//! # Repository Provider
42//!
43//! The crate provides [`SeaORMRepositoryProvider`] which implements the [`RepositoryProvider`] trait
44//! from `torii-core`, allowing it to be used directly with the main Torii authentication coordinator.
45//!
46//! # Database Support
47//!
48//! This crate can be used with any database backend supported by SeaORM:
49//! - **PostgreSQL**: Production-ready with full feature support
50//! - **MySQL**: Production-ready with full feature support
51//! - **SQLite**: Great for development and smaller deployments
52//!
53//! # Storage Implementations
54//!
55//! This crate implements repository patterns for:
56//! - User account management and profile storage
57//! - Session management with automatic expiration
58//! - Password credential storage with secure hashing
59//! - OAuth account connections and token management
60//! - WebAuthn passkey credentials and challenge handling
61//!
62//! # Entity Models
63//!
64//! The crate defines SeaORM entity models for all authentication data:
65//! - `User` - User accounts and profile information
66//! - `Session` - Active user sessions
67//! - `Password` - Hashed password credentials
68//! - `OAuthAccount` - Connected OAuth accounts
69//! - `Passkey` - WebAuthn passkey credentials
70//! - `PasskeyChallenge` - Temporary passkey challenges
71//!
72//! All entities include appropriate relationships and indexes for optimal performance.
73
74mod entities;
75mod migrations;
76mod oauth;
77mod passkey;
78mod password;
79mod session;
80mod token;
81mod user;
82
83pub mod repositories;
84pub use repositories::SeaORMRepositoryProvider;
85
86use migrations::Migrator;
87use sea_orm::{Database, DatabaseConnection};
88use sea_orm_migration::prelude::*;
89
90#[derive(Debug, thiserror::Error)]
91pub enum SeaORMStorageError {
92 #[error(transparent)]
93 Database(#[from] sea_orm::DbErr),
94 #[error("User not found")]
95 UserNotFound,
96}
97
98/// SeaORM storage backend
99///
100/// This storage backend uses SeaORM to manage database connections and migrations.
101/// It provides a `connect` method to create a new storage instance from a database URL.
102/// It also provides a `migrate` method to apply pending migrations.
103///
104/// # Example
105///
106/// ```rust,no_run
107/// use torii_storage_seaorm::SeaORMStorage;
108///
109/// #[tokio::main]
110/// async fn main() {
111/// let storage = SeaORMStorage::connect("sqlite://todos.db?mode=rwc").await.unwrap();
112/// let _ = storage.migrate().await.unwrap();
113/// }
114/// ```
115#[derive(Clone)]
116pub struct SeaORMStorage {
117 pool: DatabaseConnection,
118}
119
120impl SeaORMStorage {
121 pub fn new(pool: DatabaseConnection) -> Self {
122 Self { pool }
123 }
124
125 pub async fn connect(url: &str) -> Result<Self, SeaORMStorageError> {
126 let pool = Database::connect(url).await?;
127 pool.ping().await?;
128
129 Ok(Self::new(pool))
130 }
131
132 pub async fn migrate(&self) -> Result<(), SeaORMStorageError> {
133 Migrator::up(&self.pool, None).await.unwrap();
134
135 Ok(())
136 }
137
138 /// Create a repository provider from this storage instance
139 pub fn into_repository_provider(self) -> SeaORMRepositoryProvider {
140 SeaORMRepositoryProvider::new(self.pool)
141 }
142}
143#[cfg(test)]
144mod tests {
145 use sea_orm::Database;
146
147 use crate::migrations::Migrator;
148
149 use super::*;
150
151 #[tokio::test]
152 async fn test_migrations_up() {
153 let pool = Database::connect("sqlite::memory:").await.unwrap();
154 let migrations = Migrator::get_pending_migrations(&pool).await.unwrap();
155 migrations.iter().for_each(|m| {
156 println!("{}: {}", m.name(), m.status());
157 });
158 Migrator::up(&pool, None).await.unwrap();
159 let migrations = Migrator::get_pending_migrations(&pool).await.unwrap();
160 migrations.iter().for_each(|m| {
161 println!("{}: {}", m.name(), m.status());
162 });
163 }
164}