Skip to content

A safe authentication system made in rust from scratch.

License

Notifications You must be signed in to change notification settings

Bappoz/auth-rust

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

17 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ” Auth System Rust

Rust License PRs Welcome

Complete, secure, modular authentication system database-agnostic built with Rust and Axum.


Table of Contents


Overview

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.

Why use this system?

  • βœ… 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

✨ Features

Authentication

  • User registration with validation
  • Login with username/password
  • JWT tokens (JSON Web Tokens)
  • Route protection via middleware
  • Tokens with expiration (24 hours by default)

Security

  • Password hashing with Argon2 (OWASP recommended)
  • JWT signed with HMAC-SHA256
  • Passwords never returned in responses
  • Uniqueness validation (unique email and username)

Database

  • In-Memory - For development and testing
  • PostgreSQL - Robust relational database
  • MySQL - Compatible with MariaDB
  • SQLite - Local database
  • MongoDB - NoSQL document-based

Architecture

  • Repository Pattern - Complete decoupling
  • Trait-based - Extensible and testable
  • Async/Await - Performance with Tokio
  • Modular - Use only what you need

Arquitetura

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚             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   β”‚
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Quick Start

Prerequisites

  • Rust 1.70 or higher
  • (Optional) Database of your choice

Installation

# 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

Run with In-Memory (no database)

# Compile and run
cargo run

# The server will start at http://0.0.0.0:3000

Test the Endpoints

# 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>"

πŸ’Ύ Database Configuration

Option 1: In-Memory (Default)

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());

Option 2: PostgreSQL

Ideal for: Production, robust applications

1. Enable the feature in Cargo.toml

[features]
default = ["postgres"]

2. Configure .env

JWT_SECRET=your_secret_here
DATABASE_URL=postgresql://user:password@localhost/auth_db

3. Create the database and table

CREATE 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);

4. Uncomment the code in main.rs

See the commented section at the end of the src/main.rs file and uncomment the PostgreSQL block.


Option 3: MySQL

Ideal for: Applications already using MySQL/MariaDB

1. Enable the feature

[features]
default = ["mysql"]

2. Configure .env

DATABASE_URL=mysql://user:password@localhost/auth_db

3. Create the table

CREATE 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
);

4. Uncomment the MySQL code in main.rs


Option 4: SQLite

Ideal for: Desktop applications, small projects

1. Enable the feature

[features]
default = ["sqlite"]

2. Configure .env

DATABASE_URL=sqlite://auth.db

3. Create the table

CREATE 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
);

4. Uncomment the SQLite code in main.rs


Option 5: MongoDB

Ideal for: NoSQL applications, unstructured data

1. Enable the feature

[features]
default = ["mongodb"]

2. Configure o .env

MONGODB_URI=mongodb://localhost:27017
MONGODB_DATABASE=auth_db

3. No need to create a table!

MongoDB 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.

4. Uncomment the MongoDB code in main.rs


πŸ“‘ API Endpoints

POST /register

Register a new user.

Request Body:

{
  "username": "john",
  "email": "john@email.com",
  "password": "Password123!"
}

Response (201 Created):

{
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
}

Errors:

  • 409 Conflict - User already exists
  • 500 Internal Server Error - Processing error

POST /login

Authenticate an existing user.

Request Body:

{
  "username": "john",
  "password": "Password123!"
}

Response (200 OK):

{
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
}

Errors:

  • 401 Unauthorized - Invalid credentials

GET /private

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

πŸ“‚ Project Structure

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

πŸ”’ Security

Password Hashing

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

JWT Tokens

  • βœ… Signed with HMAC-SHA256
  • βœ… Expires in 24 hours (configurable)
  • βœ… Contains only the user ID (no sensitive data)
  • βœ… Validated on each request

Best Practices

  1. Never commit .env - Add to .gitignore
  2. Use strong secrets - Generate with openssl rand -base64 32
  3. HTTPS in production - Use TLS/SSL
  4. Rate limiting - Add brute force protection
  5. Input validation - Always validate user data

πŸ”„ How to Use in Other Projects

Method 1: Clone and Customize

  1. Clone this repository to your project
  2. Choose the database (see configuration section)
  3. Customize models and handlers as needed
  4. Run and develop!

Method 2: As Local Dependency

# 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));
}

Method 3: Create Custom Implementation

// 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
    }
    // ...
}

πŸ“ Examples

Example 1: Complete API with PostgreSQL

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 { /* ... */ })
}

Example 2: Unit Tests

#[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());
    }
}

πŸ§ͺ Testing

# Run all tests
cargo test

# Run with detailed output
cargo test -- --nocapture

# Test specific feature
cargo test --features postgres

🀝 Contributing

Contributions are welcome! Please:

  1. Fork the project
  2. Create a branch for your feature (git checkout -b feature/MyFeature)
  3. Commit your changes (git commit -m 'Add MyFeature')
  4. Push to the branch (git push origin feature/MyFeature)
  5. Open a Pull Request

Areas to Contribute

  • Add more databases (Redis, DynamoDB, etc)
  • Implement refresh tokens
  • Add 2FA (Two-Factor Authentication)
  • Rate limiting
  • Email verification
  • Password reset
  • OAuth2 integration
  • GraphQL support

πŸ“„ License

This project is licensed under the MIT License. See the LICENSE file for more details.


πŸ™ Acknowledgments


πŸ“ž Support

If you have problems or questions:

  1. Check the documentation
  2. Search for existing issues
  3. Open a new issue

πŸŽ“ Learn More


Made with ❀️ and Rust πŸ¦€

⬆ Back to top

About

A safe authentication system made in rust from scratch.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages