feat: update configuration and README for improved authentication and database support

This commit is contained in:
2026-03-05 01:49:37 +01:00
parent 9ca4eeddb4
commit 690425e144
4 changed files with 131 additions and 225 deletions

148
README.md
View File

@@ -5,10 +5,10 @@ A production-ready, modular Rust API template for K-Suite applications, followin
## Features
- **Hexagonal Architecture**: Clear separation between Domain, Infrastructure, and API layers
- **Multiple Auth Modes**: Session-based, JWT, or both - fully configurable
- **OIDC Integration**: Connect to any OpenID Connect provider (Keycloak, Auth0, Zitadel, etc.)
- **JWT-Only Authentication**: Stateless Bearer token auth — no server-side sessions
- **OIDC Integration**: Connect to any OpenID Connect provider (Keycloak, Auth0, Zitadel, etc.) with stateless cookie-based flow state
- **Database Flexibility**: SQLite (default) or PostgreSQL via feature flags
- **Type-Safe Configuration**: Newtypes with built-in validation for all security-sensitive values
- **Type-Safe Domain**: Newtypes with built-in validation for emails, passwords, secrets, and OIDC values
- **Cargo Generate Ready**: Pre-configured for scaffolding new services
## Quick Start
@@ -22,7 +22,6 @@ cargo generate --git https://github.com/GKaszewski/k-template.git
You'll be prompted to choose:
- **Project name**: Your new service name
- **Database**: `sqlite` or `postgres`
- **Session auth**: Enable cookie-based sessions
- **JWT auth**: Enable Bearer token authentication
- **OIDC**: Enable OpenID Connect integration
@@ -33,15 +32,6 @@ git clone https://github.com/GKaszewski/k-template.git my-api
cd my-api
cp .env.example .env
# Edit .env with your configuration
```
### Run
```bash
# Development (with hot reload via cargo-watch)
cargo watch -x run
# Or simply
cargo run
```
@@ -51,51 +41,51 @@ The API will be available at `http://localhost:3000/api/v1/`.
All configuration is done via environment variables. See [.env.example](.env.example) for all options.
### Authentication Modes
### Key Variables
Set `AUTH_MODE` to one of:
| Mode | Description | Required Features |
|------|-------------|-------------------|
| `session` | Cookie-based sessions | `auth-axum-login` |
| `jwt` | Bearer token authentication | `auth-jwt` |
| `both` | Try JWT first, fall back to session | `auth-axum-login`, `auth-jwt` |
| Variable | Default | Description |
|----------|---------|-------------|
| `DATABASE_URL` | `sqlite:data.db?mode=rwc` | Database connection string |
| `COOKIE_SECRET` | *(insecure dev default)* | Secret for encrypting OIDC state cookie (≥64 bytes in production) |
| `JWT_SECRET` | *(insecure dev default)* | Secret for signing JWT tokens (≥32 bytes in production) |
| `JWT_EXPIRY_HOURS` | `24` | Token lifetime in hours |
| `CORS_ALLOWED_ORIGINS` | `http://localhost:5173` | Comma-separated allowed origins |
| `SECURE_COOKIE` | `false` | Set `true` when serving over HTTPS |
| `PRODUCTION` | `false` | Enforces minimum secret lengths |
### OIDC Integration
To enable OIDC login (e.g., "Login with Google"):
To enable "Login with Google/Keycloak/etc.":
1. Enable the `auth-oidc` feature (enabled by default)
2. Configure your OIDC provider in `.env`:
1. Enable the `auth-oidc` feature (on by default in cargo-generate)
2. Set these environment variables:
```env
OIDC_ISSUER=https://your-provider.com
OIDC_CLIENT_ID=your-client-id
OIDC_CLIENT_SECRET=your-secret
OIDC_REDIRECT_URL=http://localhost:3000/api/v1/auth/callback
```
3. Users can login via `GET /api/v1/auth/login/oidc`
3. Users start the flow at `GET /api/v1/auth/login/oidc`
OIDC state (CSRF token, PKCE verifier, nonce) is stored in a short-lived encrypted cookie — no database session table required.
## Feature Flags
Features are configured in `api/Cargo.toml`:
```toml
[features]
default = ["sqlite", "auth-axum-login", "auth-oidc", "auth-jwt"]
default = ["sqlite", "auth-jwt"]
```
| Feature | Description |
|---------|-------------|
| `sqlite` | SQLite database support (default) |
| `postgres` | PostgreSQL database support |
| `auth-axum-login` | Session-based authentication |
| `sqlite` | SQLite database (default) |
| `postgres` | PostgreSQL database |
| `auth-jwt` | JWT Bearer token authentication |
| `auth-oidc` | OpenID Connect integration |
| `auth-jwt` | JWT token authentication |
| `auth-full` | All auth features combined |
### Common Configurations
**JWT-only API (stateless)**:
**JWT-only (minimal, default)**:
```toml
default = ["sqlite", "auth-jwt"]
```
@@ -105,48 +95,74 @@ default = ["sqlite", "auth-jwt"]
default = ["sqlite", "auth-oidc", "auth-jwt"]
```
**Full-featured (all auth methods)**:
**PostgreSQL + OIDC + JWT**:
```toml
default = ["sqlite", "auth-full"]
default = ["postgres", "auth-oidc", "auth-jwt"]
```
## API Endpoints
### Authentication
| Method | Endpoint | Description |
|--------|----------|-------------|
| `POST` | `/api/v1/auth/login` | Login with email/password |
| `POST` | `/api/v1/auth/register` | Register new user |
| `POST` | `/api/v1/auth/logout` | Logout (session mode) |
| `GET` | `/api/v1/auth/me` | Get current user |
| `POST` | `/api/v1/auth/token` | Get JWT for session user |
| `GET` | `/api/v1/auth/login/oidc` | Start OIDC login flow |
| `GET` | `/api/v1/auth/callback` | OIDC callback |
| Method | Endpoint | Auth | Description |
|--------|----------|------|-------------|
| `POST` | `/api/v1/auth/register` | — | Register with email + password → JWT |
| `POST` | `/api/v1/auth/login` | — | Login with email + password → JWT |
| `POST` | `/api/v1/auth/logout` | — | Returns 200; client drops the token |
| `GET` | `/api/v1/auth/me` | Bearer | Current user info |
| `POST` | `/api/v1/auth/token` | Bearer | Issue a fresh JWT (`auth-jwt`) |
| `GET` | `/api/v1/auth/login/oidc` | — | Start OIDC flow, sets encrypted state cookie (`auth-oidc`) |
| `GET` | `/api/v1/auth/callback` | — | Complete OIDC flow → JWT, clears cookie (`auth-oidc`) |
### Example: Register and use a token
```bash
# Register
curl -X POST http://localhost:3000/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "mypassword"}'
# → {"access_token": "eyJ...", "token_type": "Bearer", "expires_in": 86400}
# Use the token
curl http://localhost:3000/api/v1/auth/me \
-H "Authorization: Bearer eyJ..."
```
## Project Structure
```
k-template/
├── domain/ # Core business logic (no I/O dependencies)
├── domain/ # Pure business logic — zero I/O dependencies
│ └── src/
│ ├── entities.rs # User entity
│ ├── value_objects.rs # Email, Password, OIDC newtypes
│ ├── value_objects.rs # Email, Password, JwtSecret, OIDC newtypes
│ ├── repositories.rs # Repository interfaces (ports)
── services.rs # Domain services
── services.rs # Domain services
│ └── errors.rs # DomainError (Unauthenticated 401, Forbidden 403, …)
├── infra/ # Infrastructure adapters
│ └── src/
│ ├── auth/ # Auth backends (OIDC, JWT)
└── user_repository.rs
│ ├── auth/
│ ├── jwt.rs # JwtValidator — create + verify tokens
│ │ └── oidc.rs # OidcService + OidcState (cookie-serializable)
│ ├── user_repository.rs # SQLite / PostgreSQL adapter
│ ├── db.rs # DatabasePool re-export
│ └── factory.rs # build_user_repository()
├── api/ # HTTP API layer
├── api/ # HTTP layer
│ └── src/
│ ├── routes/ # API endpoints
├── config.rs # Configuration
└── state.rs # Application state
│ ├── routes/
│ ├── auth.rs # Login, register, logout, me, OIDC flow
│ └── config.rs # /config endpoint
│ ├── config.rs # Config::from_env()
│ ├── state.rs # AppState (user_service, cookie_key, jwt_validator, …)
│ ├── extractors.rs # CurrentUser (JWT Bearer extractor)
│ ├── error.rs # ApiError → HTTP status mapping
│ └── dto.rs # LoginRequest, RegisterRequest, TokenResponse, …
├── .env.example # Configuration template
├── migrations_sqlite/
├── migrations_postgres/
├── .env.example
└── compose.yml # Docker Compose for local dev
```
@@ -156,10 +172,13 @@ k-template/
```bash
# All tests
cargo test --all-features
cargo test
# Domain tests only
# Domain only
cargo test -p domain
# Infra only (SQLite integration tests)
cargo test -p infra
```
### Database Migrations
@@ -168,10 +187,23 @@ cargo test -p domain
# SQLite
sqlx migrate run --source migrations_sqlite
# PostgreSQL
# PostgreSQL
sqlx migrate run --source migrations_postgres
```
### Building with specific features
```bash
# Minimal: SQLite + JWT only
cargo build -F sqlite,auth-jwt
# Full: SQLite + JWT + OIDC
cargo build -F sqlite,auth-jwt,auth-oidc
# PostgreSQL variant
cargo build --no-default-features -F postgres,auth-jwt,auth-oidc
```
## License
MIT