Skip to content

Repository files navigation

AUTH

A stateless authentication service built in Rust, featuring JWT-based access and refresh token flows backed by PostgreSQL.


Table of Contents


Tech Stack

LayerTechnology
LanguageRust
Web FrameworkAxum
DatabasePostgreSQL
ORM / MigrationsSQLx
AuthenticationJWT (RS256)
Config TemplatingTera
Loggingtracing / tracing-subscriber

Project Structure

.
├── config/ # Environment-specific YAML configs
│ ├── development.yaml
│ ├── production.yaml
│ └── testing.yaml
├── migrations/ # SQLx migration files (up/down)
├── secrets/keys/ # RSA key pairs (not committed to VCS)
├── src/
│ ├── bin/main.rs # Entrypoint
│ ├── app.rs # CLI parsing and server startup
│ ├── context.rs # Shared application state (AppContext)
│ ├── config/ # Config loading and validation
│ ├── controllers/ # Route handlers
│ ├── middlewares/ # Request tracing, JSON extraction
│ ├── repository/ # Database access layer
│ ├── validator/ # Input validation
│ └── views/ # Response serialization
├── compose.yaml # Docker Compose (PostgreSQL)
└── Cargo.toml

Getting Started

Prerequisites

cargo install sqlx-cli --no-default-features --features postgres

Alternatively, spin up PostgreSQL via Docker Compose:

docker compose up -d

Installation

# Clone the repository
git clone https://github.com/<your-username>/auth.git
cd auth
# Build the project
cargo build

Configuration

The application reads configuration from config/<environment>.yaml at startup. The environment is selected via the --env CLI flag (default: development).

Config files are rendered as Tera templates, allowing environment variables to be interpolated at runtime using get_env().

Example — config/development.yaml:

server:
protocol: httphost: {{ get_env(name = "SERVER_HOST", default = "127.0.0.1") }}port: {{ get_env(name = "SERVER_PORT", default = "7150") }}database:
uri: {{ get_env(name = "DATABASE_URL", default = "postgresql://username:password@localhost:5432/database") }}max_connections: {{ get_env(name = "DATABASE_MAX_CONNECTIONS", default = "10") }}min_connections: {{ get_env(name = "DATABASE_MIN_CONNECTIONS", default = "0") }}connection_timeout: {{ get_env(name = "DATABASE_CONNECTION_TIMEOUT", default = "5") }}idle_timeout: {{ get_env(name = "DATABASE_IDLE_TIMEOUT", default = "5") }}auto_migrate: {{ get_env(name = "DATABASE_AUTO_MIGRATE", default = "true") }}dangerously_truncate: {{ get_env(name = "DATABASE_DANGEROUSLY_TRUNCATE", default = "false") }}dangerously_recreate: {{ get_env(name = "DATABASE_DANGEROUSLY_RECREATE", default = "false") }}logger:
level: debugformat: prettycrates:
- auth
- axum
- sqlx
- tower
- tower_httpauth:
access:
private_key: secrets/keys/dev/access_key.pempublic_key: secrets/keys/dev/access_key_pub.pemmaxage: 900# 15 minutesrefresh:
private_key: secrets/keys/dev/refresh_key.pempublic_key: secrets/keys/dev/refresh_key_pub.pemmaxage: 604800# 7 days

Database Flags

The following flags in the database section control migration behaviour. Use with caution outside of development:

FlagDescription
auto_migrateAutomatically run pending migrations on startup
dangerously_truncateTruncate all tables (planned — not yet implemented)
dangerously_recreateRoll back all migrations and re-run them from scratch

⚠️Warning:dangerously_recreate: true will drop and recreate all tables. Never enable this in production.


RSA Key Generation

AUTH uses RS256 (RSA + SHA-256) for signing both access and refresh JWTs. You must generate two separate key pairs — one for each token type — and place them in the secrets/keys/dev/ directory.

mkdir -p secrets/keys/dev
# Access token key pair
openssl genrsa -out secrets/keys/dev/access_key.pem 2048
openssl rsa -in secrets/keys/dev/access_key.pem \
-pubout -out secrets/keys/dev/access_key_pub.pem
# Refresh token key pair
openssl genrsa -out secrets/keys/dev/refresh_key.pem 2048
openssl rsa -in secrets/keys/dev/refresh_key.pem \
-pubout -out secrets/keys/dev/refresh_key_pub.pem

The expected directory layout after generation:

secrets/keys/dev/
├── access_key.pem
├── access_key_pub.pem
├── refresh_key.pem
└── refresh_key_pub.pem

Important: Never commit private keys to version control. Add secrets/ to your .gitignore.


Database Migrations

Migrations live in the migrations/ directory and are managed by SQLx. Each migration has an up and a down file.

Run migrations manually:

sqlx migrate run

Roll back the latest migration:

sqlx migrate revert

Alternatively, set auto_migrate: true in your config to have migrations run automatically on startup.


Running the Application

The application reads a .env file from the project root if one is present (via dotenvy). Create one to override config defaults:

DATABASE_URL=postgresql://username:password@localhost:5432/authSERVER_HOST=127.0.0.1SERVER_PORT=7150

Then start the server:

# Development (default)
cargo run
# Specify an environment explicitly
cargo run -- --env production

Available --env values: development (or dev), production (or prod), testing (or test).

On a successful start you should see:

INFO auth: Server running at http://127.0.0.1:7150

Environment Variables

All variables below have defaults defined in the config templates and are optional unless marked otherwise.

VariableDefaultDescription
SERVER_HOST127.0.0.1Host the HTTP server binds to
SERVER_PORT7150Port the HTTP server listens on
DATABASE_URLpostgresql://username:password@localhost:5432/databasePostgreSQL connection string
DATABASE_MAX_CONNECTIONS10Maximum connections in the pool
DATABASE_MIN_CONNECTIONS0Minimum idle connections in the pool
DATABASE_CONNECTION_TIMEOUT5Seconds to wait for a connection
DATABASE_IDLE_TIMEOUT5Seconds before an idle connection is closed
DATABASE_AUTO_MIGRATEtrueRun migrations on startup
DATABASE_DANGEROUSLY_TRUNCATEfalseTruncate all tables on startup
DATABASE_DANGEROUSLY_RECREATEfalseDrop and recreate schema on startup

About

An authentication implementation in Rust.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages