Introduction to Torii
Torii is a powerful authentication framework for Rust applications that gives you complete control over your users' data. Unlike hosted solutions like Auth0, Clerk, or WorkOS that store user information in their cloud, Torii lets you own and manage your authentication stack while providing modern auth features through a flexible plugin system.
Key Features
- Data Sovereignty: Your user data stays in your own database
- Multiple Authentication Methods: Support for passwords, OAuth, passkeys, and magic links
- Flexible Storage: Store user data in SQLite, PostgreSQL, or MySQL
- Extensible Plugin System: Add custom authentication methods or storage backends
Getting Started with Torii
This guide will walk you through the process of integrating Torii into your Rust application. By the end, you'll have a fully functional authentication system supporting multiple authentication methods.
Prerequisites
Before you begin, make sure you have:
- A Rust project set up with Cargo
- Basic understanding of async Rust (Torii uses
async
/await
) - Database setup for your preferred storage backend (SQLite, PostgreSQL, or MySQL)
Installation
Add Torii to your Cargo.toml
file:
[dependencies]
torii = { version = "0.1", features = ["password", "oauth", "passkey", "magic-link", "sqlite"] }
tokio = { version = "1", features = ["full"] }
The features you choose will depend on your authentication needs:
password
: Email/password authenticationoauth
: OAuth/social login supportpasskey
: WebAuthn/passkey authenticationmagic-link
: Email magic link authentication- Storage backend:
sqlite
,postgres
, or one of theseaorm-*
variants (e.g.seaorm-sqlite
,seaorm-postgres
orseaorm-mysql
)
Basic Setup
Here's a minimal example to set up Torii with a SQLite database and password authentication:
use std::sync::Arc; use torii::Torii; use torii_storage_sqlite::SqliteStorage; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { // Initialize the storage backend let storage = Arc::new(SqliteStorage::connect("sqlite:auth.db?mode=rwc").await?); // Create a Torii instance with password authentication let torii = Torii::new(storage.clone(), storage.clone()) .with_password_plugin(); // Now torii is ready to use for password-based authentication Ok(()) }
Adding Multiple Authentication Methods
Torii's plugin system makes it easy to add multiple authentication methods:
use torii::Torii; use torii_storage_sqlite::SqliteStorage; use torii_auth_oauth::providers::Provider; use std::sync::Arc; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let storage = Arc::new(SqliteStorage::connect("sqlite:auth.db").await?); let torii = Torii::new(storage.clone(), storage.clone()) // Password authentication .with_password_plugin() // OAuth providers .with_oauth_provider(Provider::google( "YOUR_GOOGLE_CLIENT_ID", "YOUR_GOOGLE_CLIENT_SECRET", "https://your-app.com/auth/callback/google" )) .with_oauth_provider(Provider::github( "YOUR_GITHUB_CLIENT_ID", "YOUR_GITHUB_CLIENT_SECRET", "https://your-app.com/auth/callback/github" )) // Passkey/WebAuthn .with_passkey_plugin( "your-app.com", // Relying Party ID "https://your-app.com" // Relying Party Origin ) // Magic Link authentication .with_magic_link_plugin(); // Now torii is ready to use with multiple authentication methods Ok(()) }
User Registration
To register a new user with password authentication:
#![allow(unused)] fn main() { async fn register_user( torii: &Torii<impl UserStorage + PasswordStorage>, email: &str, password: &str ) -> Result<(), Box<dyn std::error::Error>> { // Register a new user let user = torii.register_user_with_password(email, password).await?; println!("User registered: {}", user.id); Ok(()) } }
User Login
To authenticate a user with password:
#![allow(unused)] fn main() { async fn login_user( torii: &Torii<impl UserStorage + PasswordStorage + SessionStorage>, email: &str, password: &str ) -> Result<(), Box<dyn std::error::Error>> { // Authenticate user let (user, session) = torii.login_user_with_password(email, password).await?; // The session token can be stored in a cookie or returned to the client println!("User authenticated: {}", user.id); println!("Session token: {}", session.token); Ok(()) } }
Session Verification
To verify a user's session token:
#![allow(unused)] fn main() { async fn verify_session( torii: &Torii<impl UserStorage + SessionStorage>, session_token: &str ) -> Result<(), Box<dyn std::error::Error>> { // Verify and get session data let session = torii.get_session(session_token).await?; // Get the user associated with this session let user = torii.get_user(&session.user_id).await?; println!("Session verified for user: {}", user.id); Ok(()) } }
OAuth Authentication Flow
For OAuth authentication, you'll need to implement these steps:
- Generate an authorization URL:
#![allow(unused)] fn main() { async fn start_oauth_flow( torii: &Torii<impl UserStorage + OAuthStorage>, provider: &str ) -> Result<String, Box<dyn std::error::Error>> { // Get the authorization URL for the provider let auth_url = torii.get_oauth_authorization_url(provider).await?; // Store the CSRF state in your session/cookies let csrf_state = auth_url.csrf_state; // Return the URL to redirect the user to Ok(auth_url.url) } }
- Handle the OAuth callback:
#![allow(unused)] fn main() { async fn handle_oauth_callback( torii: &Torii<impl UserStorage + OAuthStorage + SessionStorage>, provider: &str, code: &str, state: &str ) -> Result<(), Box<dyn std::error::Error>> { // Exchange the code for tokens and authenticate the user let (user, session) = torii.exchange_oauth_code(provider, code, state).await?; println!("OAuth user authenticated: {}", user.id); println!("Session token: {}", session.token); Ok(()) } }
Next Steps
Now that you have a basic understanding of how to use Torii, you can:
- Integrate Torii with your web framework (Axum, Actix, Rocket, etc.)
- Learn about the User and Session models
- Explore each authentication method in depth
- Configure a Storage Backend for production use
Check out the remaining documentation for more detailed information on each aspect of Torii.
Core Concepts
Torii is built around several core concepts that form the foundation of the authentication system. Understanding these concepts is essential for effectively implementing and extending Torii in your applications.
Users
Users are the central entity in the Torii authentication system. Each user represents an individual who can authenticate with your application.
User Structure
The core User struct contains the following fields:
Field | Type | Description |
---|---|---|
id | UserId | The unique identifier for the user |
name | Option<String> | The user's name (optional) |
String | The user's email address | |
email_verified_at | Option<DateTime<Utc>> | Timestamp when the email was verified (if any) |
created_at | DateTime<Utc> | Timestamp when the user was created |
updated_at | DateTime<Utc> | Timestamp when the user was last updated |
User IDs
Each user has a unique UserId that identifies them in the system. This ID is:
- Stable and will not change during the user's lifetime
- Treated as an opaque string rather than a specific format (though it uses UUIDs internally by default)
- Used to link user accounts to authentication methods, sessions, and application data
OAuth Accounts
For OAuth-based authentication, Torii maintains OAuthAccount records that link a user to their external provider identities:
Field | Type | Description |
---|---|---|
user_id | UserId | The Torii user ID |
provider | String | The OAuth provider (e.g., "google") |
subject | String | The unique ID from the provider |
created_at | DateTime<Utc> | Timestamp when the link was created |
updated_at | DateTime<Utc> | Timestamp when the link was last updated |
This allows a single user to authenticate with multiple OAuth providers while maintaining a unified identity within your application.
Sessions
Sessions represent authenticated user sessions and are created when a user successfully logs in.
Session Structure
The Session struct contains the following fields:
Field | Type | Description |
---|---|---|
token | SessionToken | The unique token identifying the session |
user_id | UserId | The ID of the authenticated user |
user_agent | Option<String> | The user agent of the client that created the session |
ip_address | Option<String> | The IP address of the client that created the session |
created_at | DateTime<Utc> | Timestamp when the session was created |
updated_at | DateTime<Utc> | Timestamp when the session was last updated |
expires_at | DateTime<Utc> | Timestamp when the session will expire |
Session Tokens
Each session is identified by a unique SessionToken that:
- Functions as a bearer token or cookie for authentication
- Should be kept secret and transmitted securely (e.g. HTTPS)
- Has an expiration time after which it will no longer be valid
- Can be revoked to force a user to log out
Session Management
Torii provides several methods for managing sessions:
- Creating sessions: Generate new sessions when users log in
- Verifying sessions: Validate session tokens on subsequent requests
- Expiring sessions: Sessions automatically expire after their configured lifetime
- Revoking sessions: Explicitly invalidate sessions to force logout
- Cleaning up: Remove expired sessions to maintain database hygiene
Relationship Between Users and Sessions
In Torii:
- One-to-many relationship: A user can have multiple active sessions (login from different devices)
- Session verification: When a session token is verified, Torii returns both the session and associated user
- Session cleanup: When a user is deleted, all associated sessions are automatically removed
- User lookup: Sessions store the user ID for efficient user lookup during authentication Understanding these core concepts provides the foundation for working with Torii's authentication flows, which we'll explore in the subsequent sections.