# AuthKit
[](https://opensource.org/licenses/MIT)
[](https://www.rust-lang.org)
A better-authβinspired authentication library for Rust. Plug-and-play, framework-agnostic, and opinionated yet extensible.
## Overview
AuthKit is a Rust authentication library designed to feel like [better-auth](https://github.com/better-auth/better-auth), but for the Rust ecosystem. It provides secure, database-backed authentication with a single, unified API that works across any contextβHTTP servers, CLI tools, background workers, or proxies.
### Key Features
- **π Secure by Default** - Argon2id password hashing, timing-safe comparisons, secure token generation
- **π― Single Entry Point** - One `Auth` object for all authentication operations
- **π§ Framework-Agnostic** - Works with Axum, Actix, Rocket, or standaloneβno framework lock-in
- **πΎ Database-Backed** - SQLite and PostgreSQL support via SQLx (hidden from API)
- **π Simple API** - Register, login, verify, logoutβsame API everywhere
- **β‘ Async-First** - Built on Tokio for high-performance async operations
- **π¨ Extensible** - Swappable password and session strategies
## Quick Start
### Installation
Add AuthKit to your `Cargo.toml`:
```toml
[dependencies]
authkit = "0.1"
tokio = { version = "1.28", features = ["full"] }
```
### Basic Usage
```rust
use authkit::prelude::*;
#[tokio::main]
async fn main() -> Result<()> {
// Create an Auth instance
let auth = Auth::builder()
.database(Database::sqlite("auth.db").await?)
.build()?;
// Run migrations
auth.migrate().await?;
// Register a new user
let user = auth.register(Register {
email: "user@example.com".into(),
password: "secure-password".into(),
}).await?;
println!("User registered: {}", user.email);
// Login
let session = auth.login(Login {
email: "user@example.com".into(),
password: "secure-password".into(),
}).await?;
println!("Session token: {}", session.token);
// Verify a session
let user = auth.verify(Verify {
token: session.token.clone(),
}).await?;
println!("Verified user: {}", user.email);
// Logout
auth.logout(Logout {
token: session.token,
}).await?;
println!("Logged out successfully");
Ok(())
}
```
## Design Philosophy
AuthKit is built around five core principles:
### 1. Single Entry Point
Users interact with only one object: `Auth`. No repositories, no generics, no leaked internals.
```rust
let auth = Auth::builder()
.database(Database::sqlite("auth.db").await?)
.build()?;
```
### 2. Framework-Agnostic by Design
AuthKit doesn't depend on Axum, Actix, Rocket, Hyper, or Tower. It works equally well in:
- REST APIs
- CLI tools
- gRPC services
- Proxies (Pingora)
- Background workers
Framework adapters live outside the core library.
### 3. Opinionated Defaults, Explicit Overrides
Ships with secure defaults:
- Argon2id password hashing
- Database-backed sessions
- Secure token generation
- Sensible expiry defaults
Override behavior explicitly when needed, but never accidentally weaken security.
### 4. No Leaky Abstractions
AuthKit hides implementation details:
- SQLx internals
- Database schemas
- Crypto implementations
- Token formats
Users never interact with traits, repositories, lifetimes, or generic parameters in the public API.
### 5. Same API Everywhere
```rust
auth.register(Register { ... }).await?;
auth.login(Login { ... }).await?;
auth.verify(Verify { ... }).await?;
auth.logout(Logout { ... }).await?;
```
These calls behave identically whether invoked from an HTTP handler, CLI command, test, or background task.
## Architecture
```
Auth
βββ AuthInner (Arc)
βββ Database (trait object)
βββ PasswordStrategy
βββ SessionStrategy
βββ TokenStrategy
```
**Key characteristics:**
- `Auth` is cheap to clone (uses `Arc` internally)
- Internals are completely hidden
- Components are swappable via builder pattern
- No global state required
### Strategy Pattern
AuthKit uses a consistent strategy pattern for all authentication components:
```
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Application Layer β
β (Auth, Operations: register, login, verify, etc.) β
βββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Strategy Layer β
β β’ PasswordStrategy (Argon2, Bcrypt, etc.) β
β β’ SessionStrategy (Database-backed) β
β β’ TokenStrategy (Database-backed) β
β β
β Strategies receive &dyn DatabaseTrait as parameter β
βββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DatabaseTrait (Abstraction) β
β β
β β’ User Operations (create, find) β
β β’ Session Operations (create, find, delete) β
β β’ Token Operations (create, verify, mark used) β
βββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Backend Implementations β
β β’ SqliteDatabase (SQLite with ? params) β
β β’ PostgresDatabase (Postgres with $N params) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
**Design Benefits:**
- Strategies remain stateless and don't store database references
- Database logic is centralized in backend implementations
- Easy to add new database backends (MySQL, etc.)
- Easy to mock for testing
- No SQLx types leak into public API
## Feature Flags
AuthKit uses Cargo features for optional functionality:
```toml
[features]
default = ["sqlite", "argon2"]
# Database backends
sqlite = ["sqlx/sqlite", "sqlx/runtime-tokio"]
postgres = ["sqlx/postgres", "sqlx/runtime-tokio"]
# Password hashing strategies
argon2 = ["dep:argon2", "dep:password-hash"]
bcrypt = ["dep:bcrypt"]
# Token strategies
jwt = ["dep:jsonwebtoken"]
```
### Examples
**PostgreSQL with Argon2:**
```toml
authkit = { version = "0.1", default-features = false, features = ["postgres", "argon2"] }
```
**SQLite with bcrypt:**
```toml
authkit = { version = "0.1", default-features = false, features = ["sqlite", "bcrypt"] }
```
## Database Support
### SQLite
```rust
let auth = Auth::builder()
.database(Database::sqlite("auth.db").await?)
.build()?;
```
### PostgreSQL
```rust
let auth = Auth::builder()
.database(Database::postgres("postgresql://user:pass@localhost/authdb").await?)
.build()?;
```
### Migrations
AuthKit manages its own schema and migrations:
```rust
auth.migrate().await?;
```
**Database Schema:**
- `users` - User accounts with email and password
- `sessions` - Active user sessions
- `tokens` - Unified table for email verification, password reset, etc.
All tables include proper indexes and foreign key constraints for optimal performance and data integrity.
## API Reference
### Auth Operations
#### Register
Create a new user account:
```rust
let user = auth.register(Register {
email: "user@example.com".into(),
password: "secure-password".into(),
}).await?;
```
**Validation:**
- Email must be valid format
- Password must meet minimum security requirements
- Email must be unique
#### Login
Authenticate and create a session:
```rust
let session = auth.login(Login {
email: "user@example.com".into(),
password: "secure-password".into(),
}).await?;
```
**Returns:**
- `Session` with token, user_id, and expiration
#### Verify
Verify a session token and retrieve user:
```rust
let user = auth.verify(Verify {
token: session_token,
}).await?;
```
**Errors:**
- Invalid token
- Expired session
- Session not found
#### Logout
Invalidate a session:
```rust
auth.logout(Logout {
token: session_token,
}).await?;
```
#### Send Email Verification
Generate and optionally send a verification token for a user:
```rust
// Option 1: Manual email handling (no EmailSender configured)
let verification = auth.send_email_verification(SendEmailVerification {
user_id: user.id.clone(),
}).await?;
// You handle sending the email
send_email(&verification.email, &verification.token).await?;
// Option 2: Automatic email sending (with EmailSender configured)
// Email is sent automatically, token still returned
let verification = auth.send_email_verification(SendEmailVerification {
user_id: user.id.clone(),
}).await?;
```
**Returns:**
- `VerificationToken` with token, email, and expiration time
- Token expires in 24 hours
**Errors:**
- User not found
- Email already verified
- Email send failed (if EmailSender is configured)
#### Verify Email
Verify a user's email using a token:
```rust
let verified_user = auth.verify_email(VerifyEmail {
token: verification_token,
}).await?;
```
**Returns:**
- Updated `User` with `email_verified` set to `true`
**Errors:**
- Invalid or expired token
- Token already used
- Email already verified
#### Resend Email Verification
Resend verification token to a user:
```rust
let verification = auth.resend_email_verification(ResendEmailVerification {
email: "user@example.com".into(),
}).await?;
```
**Returns:**
- New `VerificationToken` (old tokens remain valid until used or expired)
**Errors:**
- User not found
- Email already verified
### Types
#### User
```rust
pub struct User {
pub id: String,
pub email: String,
pub created_at: i64,
pub email_verified: bool,
pub email_verified_at: Option<i64>,
}
```
#### Session
```rust
pub struct Session {
pub token: String,
pub user_id: String,
pub expires_at: i64,
}
```
#### VerificationToken
```rust
pub struct VerificationToken {
pub token: String,
pub email: String,
pub expires_at: i64,
}
```
## Email Integration
AuthKit provides a flexible email integration system that allows you to use any email service (SendGrid, AWS SES, SMTP, etc.) for sending verification emails, password reset emails, and other authentication-related emails.
### Quick Start
#### Option 1: Manual Email Handling (Default)
By default, AuthKit generates tokens but doesn't send emails. You handle email sending:
```rust
let auth = Auth::builder()
.database(Database::sqlite("auth.db").await?)
.build()?;
let verification = auth.send_email_verification(SendEmailVerification {
user_id: user.id,
}).await?;
// You send the email using your service
your_email_service::send(&verification.email, &verification.token).await?;
```
#### Option 2: Automatic Email Sending
Configure an `EmailSender` to send emails automatically:
```rust
use authkit::email::{EmailSender, EmailContext};
use async_trait::async_trait;
// Implement the EmailSender trait
struct MyEmailSender {
api_key: String,
}
#[async_trait]
impl EmailSender for MyEmailSender {
async fn send_verification_email(&self, context: EmailContext) -> Result<()> {
let url = format!("https://myapp.com/verify?token={}", context.token);
// Use your email service (SendGrid, AWS SES, SMTP, etc.)
sendgrid::send(
&self.api_key,
&context.email,
"Verify your email",
&format!("Click here: {}", url),
).await?;
Ok(())
}
}
// Configure Auth with your email sender
let auth = Auth::builder()
.database(Database::sqlite("auth.db").await?)
.email_sender(Box::new(MyEmailSender {
api_key: "your_api_key".to_string(),
}))
.build()?;
// Now emails are sent automatically
auth.send_email_verification(SendEmailVerification {
user_id: user.id,
}).await?;
```
### EmailSender Trait
```rust
#[async_trait]
pub trait EmailSender: Send + Sync {
async fn send_verification_email(&self, context: EmailContext) -> Result<()>;
}
pub struct EmailContext {
pub email: String, // Recipient's email
pub token: String, // Verification token (plaintext)
pub expires_at: i64, // Unix timestamp
}
```
### Example Implementations
#### Console Logger (Development)
```rust
struct ConsoleEmailSender {
base_url: String,
}
#[async_trait]
impl EmailSender for ConsoleEmailSender {
async fn send_verification_email(&self, context: EmailContext) -> Result<()> {
println!("π§ Verify at: {}/verify?token={}", self.base_url, context.token);
Ok(())
}
}
```
#### SendGrid (Production)
```rust
struct SendGridEmailSender {
api_key: String,
from_email: String,
base_url: String,
}
#[async_trait]
impl EmailSender for SendGridEmailSender {
async fn send_verification_email(&self, context: EmailContext) -> Result<()> {
let url = format!("{}/verify?token={}", self.base_url, context.token);
let client = reqwest::Client::new();
client
.post("https://api.sendgrid.com/v3/mail/send")
.header("Authorization", format!("Bearer {}", self.api_key))
.json(&serde_json::json!({
"personalizations": [{"to": [{"email": context.email}]}],
"from": {"email": self.from_email},
"subject": "Verify your email",
"content": [{
"type": "text/html",
"value": format!("<a href='{}'>Verify Email</a>", url)
}]
}))
.send()
.await?;
Ok(())
}
}
```
### Environment-Based Configuration
```rust
fn create_email_sender(env: &str) -> Box<dyn EmailSender> {
match env {
"production" => Box::new(SendGridEmailSender { /* ... */ }),
"development" => Box::new(ConsoleEmailSender { /* ... */ }),
_ => panic!("Unknown environment"),
}
}
let auth = Auth::builder()
.database(database)
.email_sender(create_email_sender(&env))
.build()?;
```
**For detailed email integration guide, see [docs/EMAIL_INTEGRATION.md](docs/EMAIL_INTEGRATION.md).**
## Security
### Default Security Features
| Password hashing | Argon2id |
| Timing-safe compares | β
Enabled |
| Session expiration | β
Enabled (24 hours) |
| Token entropy | High (cryptographically secure) |
| Password reuse | π« Prevented |
| Weak passwords | π« Rejected |
### Password Requirements
- Minimum length: 8 characters
- Must contain at least one uppercase letter
- Must contain at least one lowercase letter
- Must contain at least one number
- Must contain at least one special character
### Email Validation
- RFC 5322 compliant email validation
- Checks for valid format and structure
## Advanced Configuration
### Custom Password Strategy
```rust
use authkit::prelude::*;
use authkit::strategies::password::PasswordStrategyType;
let auth = Auth::builder()
.database(Database::sqlite("auth.db").await?)
.password_strategy(PasswordStrategyType::Argon2)
.build()?;
```
### Custom Session Strategy
```rust
use authkit::prelude::*;
use authkit::strategies::session::SessionStrategyType;
let auth = Auth::builder()
.database(Database::sqlite("auth.db").await?)
.session_strategy(SessionStrategyType::Database)
.build()?;
```
## Error Handling
AuthKit provides a comprehensive error type:
```rust
use authkit::prelude::*;
match auth.login(login_request).await {
Ok(session) => println!("Login successful"),
Err(AuthError::InvalidCredentials) => println!("Wrong email or password"),
Err(AuthError::UserNotFound) => println!("User doesn't exist"),
Err(e) => println!("Error: {}", e),
}
```
## Examples
Check the `examples/` directory for more usage examples:
- `email_verification.rs` - Complete email verification workflow
- `email_sender.rs` - Custom email sender implementations (SendGrid, AWS SES, SMTP, etc.)
- `basic.rs` - Simple registration and login flow (planned)
- `web_server.rs` - Integration with Axum (planned)
- `cli_tool.rs` - CLI authentication example (planned)
Run an example:
```bash
cargo run --example email_verification --features sqlite
cargo run --example email_sender --features sqlite
```
## Testing
Run the test suite:
```bash
cargo test
```
Run tests with all features:
```bash
cargo test --all-features
```
## Roadmap
### Current Status: Foundation Phase β
**Implemented:**
- β
Core Auth API
- β
Builder pattern
- β
SQLite backend
- β
PostgreSQL backend
- β
Argon2 password hashing
- β
Database sessions
- β
Email validation
- β
Password validation
- β
Token system (database-backed)
- β
Email verification flow (send, verify, resend)
**Planned:**
- π JWT sessions
- π Refresh tokens
- π Password reset flow
- π Magic link authentication
- π Axum adapter
- π Actix adapter
- π Rate limiting
- π Audit logging
- π OAuth integration
- π Two-factor authentication
## Contributing
Contributions are welcome! Please read our [contribution guidelines](AGENTS.md) first.
### For Contributors (Including AI Agents)
When contributing to AuthKit, you **MUST**:
- β
Preserve the single-entry-point design
- β
Avoid exposing generics or traits publicly
- β
Keep framework dependencies out of core
- β
Prefer composition over configuration
- β
Default to secure behavior
You **MUST NOT**:
- β Add framework-specific logic to core
- β Leak SQLx types into the public API
- β Introduce global state
- β Require users to wire repositories manually
**If a change makes the API feel less like better-auth, it's probably wrong.**
## License
This project is dual-licensed under your choice of:
- MIT License ([LICENSE-MIT](LICENSE-MIT))
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE))
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
## Acknowledgments
Inspired by [better-auth](https://github.com/better-auth/better-auth) - an excellent authentication library for JavaScript/TypeScript.
## Support
- π [Documentation](https://docs.rs/authkit)
- π [Issue Tracker](https://github.com/Akshay2642005/authkit/issues)
- π¬ [Discussions](https://github.com/Akshay2642005/authkit/discussions)
---
**Built with β€οΈ by [Akshay B](mailto:akshay2642005@gmail.com)**