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.4.0", features = ["password", "sqlite"] }
tokio = { version = "1", features = ["full"] }
The features you choose will depend on your authentication needs:
- Authentication methods:
password
: Email/password authenticationoauth
: OAuth/social login supportpasskey
: WebAuthn/passkey authenticationmagic-link
: Email magic link authentication
- Storage backends:
sqlite
: SQLite storagepostgres
: PostgreSQL storageseaorm
: SeaORM support with additional options (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_seaorm::SeaORMStorage; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { // Initialize the database connection let storage = SeaORMStorage::connect("sqlite://auth.db?mode=rwc").await?; // Run migrations to set up the database schema storage.migrate().await?; // Create repository provider and Torii instance let repositories = Arc::new(storage.into_repository_provider()); let torii = Arc::new(Torii::new(repositories)); // Now torii is ready to use for authentication Ok(()) }
Session Configuration
Torii supports two types of session management:
Opaque Sessions (Default)
Database-backed sessions with immediate revocation support:
#![allow(unused)] fn main() { use std::sync::Arc; use torii::{Torii, SessionConfig}; use chrono::Duration; let torii = Torii::new(repositories) .with_session_config( SessionConfig::default() .expires_in(Duration::days(30)) ); }
JWT Sessions
Stateless sessions with better performance for distributed systems:
#![allow(unused)] fn main() { use torii::{Torii, JwtConfig, SessionConfig}; use chrono::Duration; // Create JWT configuration let jwt_config = JwtConfig::new_hs256(b"your-secret-key-at-least-32-chars-long!".to_vec()) .with_issuer("your-app-name") .with_metadata(true); let torii = Torii::new(repositories) .with_jwt_sessions(jwt_config); // Or with custom expiration let torii = Torii::new(repositories) .with_session_config( SessionConfig::default() .with_jwt(jwt_config) .expires_in(Duration::hours(2)) ); }
Complete Example
Here's a complete example with JWT sessions and password authentication:
use std::sync::Arc; use torii::Torii; use torii_storage_seaorm::SeaORMStorage; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { // Set up database let storage = SeaORMStorage::connect("sqlite://auth.db?mode=rwc").await?; storage.migrate().await?; // Create repository provider and Torii instance let repositories = Arc::new(storage.into_repository_provider()); let torii = Arc::new(Torii::new(repositories)); // Register a user let user = torii.register_user_with_password("user@example.com", "secure_password").await?; println!("User registered: {}", user.id); // Login and create session let (user, session) = torii.login_user_with_password( "user@example.com", "secure_password", Some("Mozilla/5.0 (compatible browser)".to_string()), Some("192.168.1.100".to_string()) ).await?; println!("Login successful!"); println!("User: {}", user.id); println!("Session token: {}", session.token); // Validate session let validated_session = torii.get_session(&session.token).await?; println!("Session valid for user: {}", validated_session.user_id); Ok(()) }
Authentication Methods
The example above shows password authentication. Torii supports multiple authentication methods:
- Password Authentication: Email/password with secure hashing
- OAuth/Social Login: Google, GitHub, and other OAuth providers
- Passkey/WebAuthn: Modern biometric authentication
- Magic Link: Passwordless email-based authentication
See the feature flags in your Cargo.toml
to enable additional authentication methods.
User Registration
To register a new user with password authentication:
#![allow(unused)] fn main() { use torii::{Torii, ToriiError}; use torii_core::RepositoryProvider; async fn register_user( torii: &Torii<impl RepositoryProvider>, email: &str, password: &str ) -> Result<(), ToriiError> { // 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() { use torii::{Torii, ToriiError}; use torii_core::RepositoryProvider; async fn login_user( torii: &Torii<impl RepositoryProvider>, email: &str, password: &str ) -> Result<(), ToriiError> { // Authenticate user - optional user_agent and ip_address for tracking let (user, session) = torii.login_user_with_password( email, password, Some("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36".to_string()), Some("127.0.0.1".to_string()) ).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() { use torii::{Torii, SessionToken, ToriiError}; use torii_core::RepositoryProvider; async fn verify_session( torii: &Torii<impl RepositoryProvider>, session_token: &str ) -> Result<(), ToriiError> { // Parse the session token let token = SessionToken::new(session_token); // Verify and get session data (works for both JWT and opaque tokens) let session = torii.get_session(&token).await?; // Get the user associated with this session let user = torii.get_user(&session.user_id).await? .ok_or_else(|| ToriiError::AuthError("User not found".to_string()))?; 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() { use torii::{Torii, ToriiError}; async fn start_oauth_flow( torii: &Torii<impl torii_core::storage::UserStorage + torii_core::storage::OAuthStorage>, provider: &str ) -> Result<String, ToriiError> { // 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() { use torii::{Torii, ToriiError}; async fn handle_oauth_callback( torii: &Torii<impl torii_core::storage::UserStorage + torii_core::storage::OAuthStorage>, provider: &str, code: &str, state: &str ) -> Result<(), ToriiError> { // Exchange the code for tokens and authenticate the user let (user, session) = torii.exchange_oauth_code( provider, code, state, Some("Browser User Agent".to_string()), Some("127.0.0.1".to_string()) ).await?; println!("OAuth user authenticated: {}", user.id); println!("Session token: {}", session.token); Ok(()) } }
Passkey Authentication
Passkey (WebAuthn) authentication is performed in two steps:
- Start registration:
#![allow(unused)] fn main() { use torii::{Torii, ToriiError}; async fn start_passkey_registration( torii: &Torii<impl torii_core::storage::UserStorage + torii_core::storage::PasskeyStorage>, email: &str ) -> Result<(), ToriiError> { // Begin the passkey registration let options = torii.begin_passkey_registration(email).await?; // Return the challenge ID and WebAuthn options to the client for processing println!("Challenge ID: {}", options.challenge_id); println!("WebAuthn Options: {}", serde_json::to_string(&options.options).unwrap()); Ok(()) } }
- Complete registration:
#![allow(unused)] fn main() { use torii::{Torii, ChallengeId, PasskeyRegistrationCompletion, ToriiError}; async fn complete_passkey_registration( torii: &Torii<impl torii_core::storage::UserStorage + torii_core::storage::PasskeyStorage>, email: &str, challenge_id: &str, response: serde_json::Value ) -> Result<(), ToriiError> { // Complete the passkey registration let completion = PasskeyRegistrationCompletion { email: email.to_string(), challenge_id: ChallengeId::new(challenge_id.to_string()), response, }; let user = torii.complete_passkey_registration(&completion).await?; println!("User registered with passkey: {}", user.id); Ok(()) } }
Magic Link Authentication
Magic link authentication is useful for passwordless email-based login:
#![allow(unused)] fn main() { use torii::{Torii, ToriiError}; async fn send_magic_link( torii: &Torii<impl torii_core::storage::UserStorage + torii_core::storage::MagicLinkStorage>, email: &str ) -> Result<(), ToriiError> { // Generate a magic token let token = torii.generate_magic_token(email).await?; // Create a magic link URL (this would typically be sent via email) let magic_link = format!("https://your-app.com/auth/magic-link?token={}", token.token); println!("Magic Link: {}", magic_link); Ok(()) } async fn verify_magic_link( torii: &Torii<impl torii_core::storage::UserStorage + torii_core::storage::MagicLinkStorage>, token: &str ) -> Result<(), ToriiError> { // Verify the magic token and create a session let (user, session) = torii.verify_magic_token( token, Some("Browser User Agent".to_string()), Some("127.0.0.1".to_string()) ).await?; println!("User authenticated via magic link: {}", user.id); println!("Session token: {}", session.token); Ok(()) } }
Web Framework Integration
Axum Integration
For Axum web applications, use the torii-axum
crate for plug-and-play authentication:
[dependencies]
torii-axum = { version = "0.4.0", features = ["password", "sqlite"] }
use std::sync::Arc; use axum::{response::Json, routing::get, Router}; use torii::Torii; use torii_axum::{AuthUser, CookieConfig}; use torii_storage_seaorm::SeaORMStorage; #[tokio::main] async fn main() -> anyhow::Result<()> { // Set up database and Torii let storage = SeaORMStorage::connect("sqlite::memory:").await?; storage.migrate().await?; let repositories = Arc::new(storage.into_repository_provider()); let torii = Arc::new(Torii::new(repositories)); // Create authentication routes with cookie configuration let auth_routes = torii_axum::routes(torii.clone()) .with_cookie_config(CookieConfig::development()) .build(); // Build your application with auth routes let app = Router::new() .nest("/auth", auth_routes) .route("/protected", get(protected_handler)) .with_state(torii); // Start server let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?; axum::serve(listener, app).await?; Ok(()) } // Protected route handler async fn protected_handler(user: AuthUser) -> Json<serde_json::Value> { Json(serde_json::json!({ "user_id": user.id, "email": user.email })) }
This provides automatic endpoints for:
POST /auth/register
- User registrationPOST /auth/login
- User loginGET /auth/user
- Get current userPOST /auth/logout
- User logout- And more...
Example Applications
You can find complete examples in the repository:
- examples/axum-sqlite-password - Axum integration with SQLite and password authentication
- examples/todos - Complete todo application demonstrating Torii integration
These examples demonstrate:
- Setting up Torii with different storage backends
- Adding password authentication
- Creating web servers with Axum
- Implementing user registration and login
- Managing authenticated sessions with cookies
Next Steps
Now that you have a basic understanding of how to use Torii, you can:
- Learn about Session Management - Choose between opaque and JWT sessions
- Configure JWT Sessions for stateless authentication
- Use Opaque Sessions for traditional session management
- Integrate Torii with your web framework (Axum, Actix, Rocket, etc.)
- Learn about the Core Concepts of Users and Sessions
- Explore each authentication method in more depth
- Configure a storage backend for production use
Remember that Torii is designed to give you flexibility while maintaining control over your user data.
Quick Reference
JWT Sessions (Stateless)
#![allow(unused)] fn main() { let jwt_config = JwtConfig::new_hs256(secret_key.to_vec()); let torii = Torii::new(repositories).with_jwt_sessions(jwt_config); }
Opaque Sessions (Stateful - Default)
#![allow(unused)] fn main() { let torii = Torii::new(repositories); // Default uses opaque sessions }
Environment Variables for Production
export JWT_SECRET="your-secret-key-at-least-32-characters-long"
export DATABASE_URL="sqlite://production.db"