Complete, secure, modular authentication system database-agnostic built with Rust and Axum.
- Overview
- Features
- Architecture
- Quick Start
- Database Configuration
- API Endpoints
- Project Structure
- Security
- How to Use in Other Projects
- Examples
- Testing
- Contributing
- License
This is a production-ready authentication system that can be easily integrated into any Rust project. The key feature is complete database independence, allowing you to choose (or switch) databases without changing a single line of business logic code.
- β Database Agnostic - Use PostgreSQL, MySQL, SQLite, MongoDB or even in-memory
- β Security First - Argon2 for password hashing, JWT for tokens
- β Modular and Reusable - Clone and use in any project
- β Type-Safe - Leverage Rust's type safety
- β Async/Await - Maximum performance with Tokio
- β Production Ready - Robust error handling
- β Easy to Extend - Add new databases by implementing a trait
- User registration with validation
- Login with username/password
- JWT tokens (JSON Web Tokens)
- Route protection via middleware
- Tokens with expiration (24 hours by default)
- Password hashing with Argon2 (OWASP recommended)
- JWT signed with HMAC-SHA256
- Passwords never returned in responses
- Uniqueness validation (unique email and username)
- In-Memory - For development and testing
- PostgreSQL - Robust relational database
- MySQL - Compatible with MariaDB
- SQLite - Local database
- MongoDB - NoSQL document-based
- Repository Pattern - Complete decoupling
- Trait-based - Extensible and testable
- Async/Await - Performance with Tokio
- Modular - Use only what you need
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β HTTP Layer (Axum Handlers) β
β βββββββββββββββ ββββββββββββββββ ββββββββββββββ β
β β Register β β Login β β Protected β β
β β Handler β β Handler β β Routes β β
β βββββββββββββββ ββββββββββββββββ ββββββββββββββ β
ββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Business Logic Layer (Services) β
β ββββββββββββββββ ββββββββββββββββ βββββββββββββ β
β β JWT Service β βCrypto Serviceβ βAuth Logic β β
β ββββββββββββββββ ββββββββββββββββ βββββββββββββ β
ββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Repository Layer (UserRepository Trait) β
β Trait-based Abstraction β
ββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββ
β
ββββββββββββββΌβββββββββββββ
β β β
βΌ βΌ βΌ
ββββββββββββ ββββββββββββ ββββββββββββ
βPostgreSQLβ β MySQL β β MongoDB β
β Impl β β Impl β β Impl β
ββββββββββββ ββββββββββββ ββββββββββββ
- Rust 1.70 or higher
- (Optional) Database of your choice
# Clone the repository
git clone https://github.com/seu-usuario/auth-system-rust.git
cd auth-system-rust
# Copy the example .env file
cp .env.example .env
# Edit .env and configure your JWT_SECRET
# You can generate one with: openssl rand -base64 32
nano .env# Compile and run
cargo run
# The server will start at http://0.0.0.0:3000# 1. Register a new user
curl -X POST http://localhost:3000/register \
-H "Content-Type: application/json" \
-d '{
"username": "john",
"email": "john@email.com",
"password": "Password123!"
}'
# Response: {"token":"eyJ0eXAiOiJKV1QiLCJhbGc..."}
# 2. Login
curl -X POST http://localhost:3000/login \
-H "Content-Type: application/json" \
-d '{
"username": "john",
"password": "Password123!"
}'
# 3. Access protected route (use the received token)
curl -X GET http://localhost:3000/private \
-H "Authorization: Bearer YOUR_TOKEN_HERE"
# Response: "Access granted for user: <user_id>"Ideal for: Development, testing, prototypes
Configuration: None! It's ready to use.
Warning: Data is lost when the process ends.
// Already configured in main.rs
let user_repo = Arc::new(InMemoryUserRepository::new());Ideal for: Production, robust applications
[features]
default = ["postgres"]JWT_SECRET=your_secret_here
DATABASE_URL=postgresql://user:password@localhost/auth_dbCREATE DATABASE auth_db;
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
is_active BOOLEAN DEFAULT TRUE
);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_username ON users(username);See the commented section at the end of the src/main.rs file and uncomment the PostgreSQL block.
Ideal for: Applications already using MySQL/MariaDB
[features]
default = ["mysql"]DATABASE_URL=mysql://user:password@localhost/auth_dbCREATE TABLE users (
id CHAR(36) PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
is_active BOOLEAN DEFAULT TRUE
);Ideal for: Desktop applications, small projects
[features]
default = ["sqlite"]DATABASE_URL=sqlite://auth.dbCREATE TABLE users (
id TEXT PRIMARY KEY,
username TEXT UNIQUE NOT NULL,
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
is_active INTEGER DEFAULT 1
);Ideal for: NoSQL applications, unstructured data
[features]
default = ["mongodb"]MONGODB_URI=mongodb://localhost:27017
MONGODB_DATABASE=auth_dbMongoDB creates the collection automatically. Optionally, you can create indexes for better performance:
# Option A: Run setup script (creates indexes)
cargo run --example mongodb_setup --features mongodb
# Option B: MongoDB creates everything automatically on first use
# Simply run the application!Note: MongoDB is schema-less (no fixed schema), so it doesn't need migrations like SQL databases.
Register a new user.
Request Body:
{
"username": "john",
"email": "john@email.com",
"password": "Password123!"
}Response (201 Created):
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
}Errors:
409 Conflict- User already exists500 Internal Server Error- Processing error
Authenticate an existing user.
Request Body:
{
"username": "john",
"password": "Password123!"
}Response (200 OK):
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
}Errors:
401 Unauthorized- Invalid credentials
Protected route (requires authentication).
Headers:
Authorization: Bearer <your_jwt_token>
Response (200 OK):
Access granted for user: <user_id>
Errors:
401 Unauthorized- Invalid, expired or missing token
auth-system/
βββ Cargo.toml # Dependencies and configurations
βββ .env # Environment variables (do not commit!)
βββ .env.example # Configuration example
βββ README.md # This documentation
β
βββ src/
β βββ lib.rs # Main library (AppState)
β βββ main.rs # Entry point (HTTP server)
β βββ errors.rs # Custom error types
β β
β βββ auth/ # Authentication module
β β βββ mod.rs
β β βββ crypto.rs # Hash/verification of passwords (Argon2)
β β βββ jwt.rs # JWT creation/validation
β β βββ extractor.rs # Authenticated user extractor (Axum)
β β
β βββ db/ # Database layer
β β βββ mod.rs
β β βββ user_repository.rs # Trait (interface)
β β βββ memory_connection.rs # In-memory implementation
β β βββ postgres_connection.rs # PostgreSQL implementation
β β βββ mysql_connection.rs # MySQL implementation
β β βββ sqlite_connection.rs # SQLite implementation
β β βββ mongodb_connection.rs # MongoDB implementation
β β
β βββ models/ # Data models
β β βββ mod.rs
β β βββ user.rs # User, CreateUser
β β βββ auth.rs # LoginRequest, RegisterRequest, LoginResponse
β β
β βββ handlers/ # HTTP Handlers
β βββ mod.rs
β βββ auth_handler.rs # register_handler, login_handler
β
βββ migrations/ # SQL migrations (optional)
βββ 001_create_users.sql
We use Argon2, winner of the Password Hashing Competition and recommended by OWASP:
- β Resistant to brute force attacks
- β Resistant to GPU/ASIC attacks
- β Unique salt per password
- β Secure settings by default
- β Signed with HMAC-SHA256
- β Expires in 24 hours (configurable)
- β Contains only the user ID (no sensitive data)
- β Validated on each request
- Never commit
.env- Add to.gitignore - Use strong secrets - Generate with
openssl rand -base64 32 - HTTPS in production - Use TLS/SSL
- Rate limiting - Add brute force protection
- Input validation - Always validate user data
- Clone this repository to your project
- Choose the database (see configuration section)
- Customize models and handlers as needed
- Run and develop!
# Your project/Cargo.toml
[dependencies]
auth-system = { path = "../auth-system" }// Your project/src/main.rs
use auth_system::{AppState, handlers::auth_handler};
use auth_system::db::postgres_connection::PostgresUserRepository;
#[tokio::main]
async fn main() {
// Configure your database
let user_repo = Arc::new(PostgresUserRepository::new(pool));
let state = AppState {
jwt_secret: "...".into(),
user_repo,
};
// Use the ready-made handlers!
let app = Router::new()
.route("/register", post(auth_handler::register_handler))
.route("/login", post(auth_handler::login_handler));
}// Your project/src/db/custom_repository.rs
use async_trait::async_trait;
use auth_system::db::user_repository::UserRepository;
struct MyRepository {
// Your implementation
}
#[async_trait]
impl UserRepository for MyRepository {
// Implement the methods
async fn create(...) -> Result<User, AuthError> {
// Your logic
}
// ...
}use auth_system::{AppState, handlers::auth_handler};
use auth_system::db::postgres_connection::PostgresUserRepository;
use sqlx::postgres::PgPoolOptions;
#[tokio::main]
async fn main() {
dotenv().ok();
let db_pool = PgPoolOptions::new()
.max_connections(5)
.connect(&std::env::var("DATABASE_URL").unwrap())
.await
.unwrap();
let user_repo = Arc::new(PostgresUserRepository::new(db_pool));
let state = AppState {
jwt_secret: std::env::var("JWT_SECRET").unwrap(),
user_repo,
};
let app = Router::new()
.route("/register", post(auth_handler::register_handler))
.route("/login", post(auth_handler::login_handler))
.route("/profile", get(profile_handler)) // Custom handler
.with_state(state);
// ... server
}
// Custom handler that uses AuthUser
async fn profile_handler(user: AuthUser) -> Json<UserProfile> {
// user.user_id contains the authenticated user's ID
// Fetch additional data and return
Json(UserProfile { /* ... */ })
}#[cfg(test)]
mod tests {
use super::*;
use auth_system::db::memory_connection::InMemoryUserRepository;
#[tokio::test]
async fn test_register_success() {
let user_repo = Arc::new(InMemoryUserRepository::new());
let state = AppState {
jwt_secret: "test_secret".into(),
user_repo,
};
let request = RegisterRequest {
username: "test".into(),
email: "test@test.com".into(),
password: "Password123!".into(),
};
let result = register_handler(State(state), Json(request)).await;
assert!(result.is_ok());
}
}# Run all tests
cargo test
# Run with detailed output
cargo test -- --nocapture
# Test specific feature
cargo test --features postgresContributions are welcome! Please:
- Fork the project
- Create a branch for your feature (
git checkout -b feature/MyFeature) - Commit your changes (
git commit -m 'Add MyFeature') - Push to the branch (
git push origin feature/MyFeature) - Open a Pull Request
- Add more databases (Redis, DynamoDB, etc)
- Implement refresh tokens
- Add 2FA (Two-Factor Authentication)
- Rate limiting
- Email verification
- Password reset
- OAuth2 integration
- GraphQL support
This project is licensed under the MIT License. See the LICENSE file for more details.
- Axum - Web framework
- SQLx - SQL toolkit
- jsonwebtoken - JWT implementation
- argon2 - Password hashing
If you have problems or questions:
- Check the documentation
- Search for existing issues
- Open a new issue
Made with β€οΈ and Rust π¦