| Crates.io | authkit |
| lib.rs | authkit |
| version | 0.1.2 |
| created_at | 2026-01-10 03:42:48.18225+00 |
| updated_at | 2026-01-16 16:23:50.831639+00 |
| description | A better-auth inspired authentication library for Rust. Framework-agnostic, secure by default, with database-backed sessions and email verification. |
| homepage | https://github.com/Akshay2642005/authkit |
| repository | https://github.com/Akshay2642005/authkit |
| max_upload_size | |
| id | 2033412 |
| size | 268,805 |
A better-authβinspired authentication library for Rust. Plug-and-play, framework-agnostic, and opinionated yet extensible.
AuthKit is a Rust authentication library designed to feel like 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.
Auth object for all authentication operationsAdd AuthKit to your Cargo.toml:
[dependencies]
authkit = "0.1"
tokio = { version = "1.28", features = ["full"] }
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(())
}
AuthKit is built around five core principles:
Users interact with only one object: Auth. No repositories, no generics, no leaked internals.
let auth = Auth::builder()
.database(Database::sqlite("auth.db").await?)
.build()?;
AuthKit doesn't depend on Axum, Actix, Rocket, Hyper, or Tower. It works equally well in:
Framework adapters live outside the core library.
Ships with secure defaults:
Override behavior explicitly when needed, but never accidentally weaken security.
AuthKit hides implementation details:
Users never interact with traits, repositories, lifetimes, or generic parameters in the public API.
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.
Auth
βββ AuthInner (Arc)
βββ Database (trait object)
βββ PasswordStrategy
βββ SessionStrategy
βββ TokenStrategy
Key characteristics:
Auth is cheap to clone (uses Arc internally)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:
AuthKit uses Cargo features for optional functionality:
[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"]
PostgreSQL with Argon2:
authkit = { version = "0.1", default-features = false, features = ["postgres", "argon2"] }
SQLite with bcrypt:
authkit = { version = "0.1", default-features = false, features = ["sqlite", "bcrypt"] }
let auth = Auth::builder()
.database(Database::sqlite("auth.db").await?)
.build()?;
let auth = Auth::builder()
.database(Database::postgres("postgresql://user:pass@localhost/authdb").await?)
.build()?;
AuthKit manages its own schema and migrations:
auth.migrate().await?;
Database Schema:
users - User accounts with email and passwordsessions - Active user sessionstokens - Unified table for email verification, password reset, etc.All tables include proper indexes and foreign key constraints for optimal performance and data integrity.
Create a new user account:
let user = auth.register(Register {
email: "user@example.com".into(),
password: "secure-password".into(),
}).await?;
Validation:
Authenticate and create a session:
let session = auth.login(Login {
email: "user@example.com".into(),
password: "secure-password".into(),
}).await?;
Returns:
Session with token, user_id, and expirationVerify a session token and retrieve user:
let user = auth.verify(Verify {
token: session_token,
}).await?;
Errors:
Invalidate a session:
auth.logout(Logout {
token: session_token,
}).await?;
Generate and optionally send a verification token for a user:
// 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 timeErrors:
Verify a user's email using a token:
let verified_user = auth.verify_email(VerifyEmail {
token: verification_token,
}).await?;
Returns:
User with email_verified set to trueErrors:
Resend verification token to a user:
let verification = auth.resend_email_verification(ResendEmailVerification {
email: "user@example.com".into(),
}).await?;
Returns:
VerificationToken (old tokens remain valid until used or expired)Errors:
pub struct User {
pub id: String,
pub email: String,
pub created_at: i64,
pub email_verified: bool,
pub email_verified_at: Option<i64>,
}
pub struct Session {
pub token: String,
pub user_id: String,
pub expires_at: i64,
}
pub struct VerificationToken {
pub token: String,
pub email: String,
pub expires_at: i64,
}
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.
By default, AuthKit generates tokens but doesn't send emails. You handle email sending:
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?;
Configure an EmailSender to send emails automatically:
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?;
#[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
}
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(())
}
}
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(())
}
}
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.
| Feature | Default |
|---|---|
| Password hashing | Argon2id |
| Timing-safe compares | β Enabled |
| Session expiration | β Enabled (24 hours) |
| Token entropy | High (cryptographically secure) |
| Password reuse | π« Prevented |
| Weak passwords | π« Rejected |
use authkit::prelude::*;
use authkit::strategies::password::PasswordStrategyType;
let auth = Auth::builder()
.database(Database::sqlite("auth.db").await?)
.password_strategy(PasswordStrategyType::Argon2)
.build()?;
use authkit::prelude::*;
use authkit::strategies::session::SessionStrategyType;
let auth = Auth::builder()
.database(Database::sqlite("auth.db").await?)
.session_strategy(SessionStrategyType::Database)
.build()?;
AuthKit provides a comprehensive error type:
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),
}
Check the examples/ directory for more usage examples:
email_verification.rs - Complete email verification workflowemail_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:
cargo run --example email_verification --features sqlite
cargo run --example email_sender --features sqlite
Run the test suite:
cargo test
Run tests with all features:
cargo test --all-features
Implemented:
Planned:
Contributions are welcome! Please read our contribution guidelines first.
When contributing to AuthKit, you MUST:
You MUST NOT:
If a change makes the API feel less like better-auth, it's probably wrong.
This project is dual-licensed under your choice of:
at your option.
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.
Inspired by better-auth - an excellent authentication library for JavaScript/TypeScript.
Built with β€οΈ by Akshay B